rlsl 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 020a239d452eb3a27b9a40105c7c24286af2fbf4d827e2e934575008ba51c495
4
+ data.tar.gz: fa2f50452cfdf52213e63058a0f91ae0aac4fe94650fc091c571895e897d5d35
5
+ SHA512:
6
+ metadata.gz: c2014e38c0e9f741104653c4196559f58402dc02c12ab5ac6f622f9c080b951a623b511eae7749538bbb0e601ca784c8e9f27834cb196fd20a51d62c16321e85
7
+ data.tar.gz: 1b4a13bd1b2bb87a6cbe2d2d0be5c72840b3f5c43dce88ec4a5b3cfa529c0ad2ea1fb71c5a1b3b747e430f91be12b8817cc22cc380e028e1847057bdb785db8e
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.1.0 - 2026-01-04
6
+
7
+ - Initial release.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # RLSL
2
+
3
+ Ruby Like Shading Language - A Ruby DSL for writing shaders that transpile to multiple GPU shader languages.
4
+
5
+ ## Features
6
+
7
+ - Write shaders using Ruby syntax
8
+ - Transpile to multiple targets:
9
+ - GLSL (OpenGL Shading Language)
10
+ - WGSL (WebGPU Shading Language)
11
+ - MSL (Metal Shading Language)
12
+ - C (for CPU-based rendering)
13
+ - Type inference for shader variables
14
+ - Support for common shader operations (vec2, vec3, vec4, etc.)
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'rlsl'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ ```bash
27
+ $ bundle install
28
+ ```
29
+
30
+ Or install it yourself as:
31
+
32
+ ```bash
33
+ $ gem install rlsl
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ### Basic Example
39
+
40
+ ```ruby
41
+ require 'rlsl'
42
+
43
+ # Generate GLSL shader
44
+ glsl_code = RLSL.to_glsl(:my_shader) do
45
+ uniforms do
46
+ float :time
47
+ end
48
+
49
+ fragment do |frag_coord, resolution, u|
50
+ # Normalize coordinates
51
+ uv = frag_coord / resolution
52
+
53
+ # Create color based on position and time
54
+ r = sin(u.time + uv.x * 6.28) * 0.5 + 0.5
55
+ g = sin(u.time + uv.y * 6.28) * 0.5 + 0.5
56
+ b = sin(u.time) * 0.5 + 0.5
57
+
58
+ vec3(r, g, b)
59
+ end
60
+ end
61
+
62
+ puts glsl_code
63
+ ```
64
+
65
+ ### Generate WGSL (WebGPU)
66
+
67
+ ```ruby
68
+ wgsl_code = RLSL.to_wgsl(:my_shader) do
69
+ uniforms do
70
+ float :time
71
+ vec2 :mouse
72
+ end
73
+
74
+ fragment do |frag_coord, resolution, u|
75
+ uv = frag_coord / resolution.y
76
+ color = vec3(uv.x, uv.y, sin(u.time) * 0.5 + 0.5)
77
+ color
78
+ end
79
+ end
80
+ ```
81
+
82
+ ### Generate MSL (Metal)
83
+
84
+ ```ruby
85
+ metal_shader = RLSL.define_metal(:my_shader) do
86
+ uniforms do
87
+ float :time
88
+ end
89
+
90
+ fragment do |frag_coord, resolution, u|
91
+ vec3(1.0, 0.0, 0.0)
92
+ end
93
+ end
94
+ ```
95
+
96
+ ### Using Helper Functions
97
+
98
+ ```ruby
99
+ RLSL.to_glsl(:complex_shader) do
100
+ uniforms do
101
+ float :time
102
+ end
103
+
104
+ functions do
105
+ float :noise
106
+ vec3 :get_color
107
+ end
108
+
109
+ helpers(:ruby) do
110
+ def noise(p)
111
+ sin(p.x * 12.9898 + p.y * 78.233) * 43758.5453
112
+ end
113
+
114
+ def get_color(uv, t)
115
+ vec3(uv.x, uv.y, sin(t) * 0.5 + 0.5)
116
+ end
117
+ end
118
+
119
+ fragment do |frag_coord, resolution, u|
120
+ uv = frag_coord / resolution
121
+ get_color(uv, u.time)
122
+ end
123
+ end
124
+ ```
125
+
126
+ ## Supported Types
127
+
128
+ - `bool` - Boolean (conditional logic)
129
+ - `int` - Integer (loop counters, array indices)
130
+ - `float` - Scalar floating point
131
+ - `vec2` - 2D vector
132
+ - `vec3` - 3D vector
133
+ - `vec4` - 4D vector
134
+ - `mat4` - 4x4 matrix (MVP transformations)
135
+ - `mat3` - 3x3 matrix (normal transformations)
136
+ - `mat2` - 2x2 matrix (2D texture coordinate transformations)
137
+ - `sampler2D` - 2D texture sampler
138
+
139
+ ## Built-in Functions
140
+
141
+ RLSL supports common shader functions:
142
+
143
+ - Math: `sin`, `cos`, `tan`, `sqrt`, `pow`, `exp`, `log`, `abs`, `floor`, `ceil`
144
+ - Vector: `normalize`, `length`, `dot`, `cross`, `reflect`, `refract`
145
+ - Interpolation: `mix`, `clamp`, `smoothstep`
146
+ - Other: `fract`, `min`, `max`
147
+
148
+ ## Constants
149
+
150
+ - `PI` - 3.14159265358979323846
151
+ - `TAU` - 6.28318530717958647692
152
+
153
+ ## Requirements
154
+
155
+ - Ruby >= 3.1.0
156
+ - [Prism](https://github.com/ruby/prism) gem (for Ruby parsing)
157
+ - [metaco](https://github.com/ydah/metaco) gem (for Metal shader execution)
158
+
159
+ ## Development
160
+
161
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests.
162
+
163
+ ```bash
164
+ $ bundle install
165
+ $ rake test
166
+ ```
167
+
168
+ ## License
169
+
170
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class BaseTranslator
5
+ FUNC_REPLACEMENTS = [].freeze
6
+
7
+ TYPE_MAP = {}.freeze
8
+
9
+ def initialize(uniforms, helpers_code, fragment_code)
10
+ @uniforms = uniforms
11
+ @helpers_code = helpers_code || ""
12
+ @fragment_code = fragment_code || ""
13
+ end
14
+
15
+ def translate
16
+ helpers_translated = translate_code(@helpers_code)
17
+ fragment_translated = translate_code(@fragment_code)
18
+ generate_shader(helpers_translated, fragment_translated)
19
+ end
20
+
21
+ protected
22
+
23
+ def translate_code(c_code)
24
+ return "" if c_code.nil? || c_code.empty?
25
+
26
+ result = c_code.dup
27
+
28
+ self.class::TYPE_MAP.each do |c_type, target_type|
29
+ result.gsub!(/\b#{c_type}\b/, target_type)
30
+ end
31
+
32
+ self.class::FUNC_REPLACEMENTS.each do |pattern, replacement|
33
+ result.gsub!(pattern, replacement)
34
+ end
35
+
36
+ result
37
+ end
38
+
39
+ def generate_shader(_helpers, _fragment)
40
+ raise NotImplementedError, "Subclasses must implement generate_shader"
41
+ end
42
+
43
+ def self.common_func_replacements(target_vec2:, target_vec3:, target_vec4:)
44
+ [
45
+ [/vec2_new\(([^,]+),\s*([^)]+)\)/, "#{target_vec2}(\\1, \\2)"],
46
+ [/vec3_new\(([^,]+),\s*([^,]+),\s*([^)]+)\)/, "#{target_vec3}(\\1, \\2, \\3)"],
47
+ [/vec4_new\(([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)/, "#{target_vec4}(\\1, \\2, \\3, \\4)"],
48
+ [/vec2_add\(([^,]+),\s*([^)]+)\)/, '(\1 + \2)'],
49
+ [/vec3_add\(([^,]+),\s*([^)]+)\)/, '(\1 + \2)'],
50
+ [/vec2_sub\(([^,]+),\s*([^)]+)\)/, '(\1 - \2)'],
51
+ [/vec3_sub\(([^,]+),\s*([^)]+)\)/, '(\1 - \2)'],
52
+ [/vec2_mul\(([^,]+),\s*([^)]+)\)/, '(\1 * \2)'],
53
+ [/vec3_mul\(([^,]+),\s*([^)]+)\)/, '(\1 * \2)'],
54
+ [/vec2_div\(([^,]+),\s*([^)]+)\)/, '(\1 / \2)'],
55
+ [/vec3_div\(([^,]+),\s*([^)]+)\)/, '(\1 / \2)'],
56
+ [/vec2_dot\(([^,]+),\s*([^)]+)\)/, 'dot(\1, \2)'],
57
+ [/vec3_dot\(([^,]+),\s*([^)]+)\)/, 'dot(\1, \2)'],
58
+ [/vec2_length\(([^)]+)\)/, 'length(\1)'],
59
+ [/vec3_length\(([^)]+)\)/, 'length(\1)'],
60
+ [/vec2_normalize\(([^)]+)\)/, 'normalize(\1)'],
61
+ [/vec3_normalize\(([^)]+)\)/, 'normalize(\1)'],
62
+ [/sqrtf\(/, "sqrt("],
63
+ [/sinf\(/, "sin("],
64
+ [/cosf\(/, "cos("],
65
+ [/tanf\(/, "tan("],
66
+ [/fabsf\(/, "abs("],
67
+ [/fminf\(/, "min("],
68
+ [/fmaxf\(/, "max("],
69
+ [/floorf\(/, "floor("],
70
+ [/ceilf\(/, "ceil("],
71
+ [/powf\(/, "pow("],
72
+ [/expf\(/, "exp("],
73
+ [/logf\(/, "log("],
74
+ [/atan2f\(/, "atan2("],
75
+ [/fmodf\(/, "fmod("],
76
+ [/mix_f\(/, "mix("],
77
+ [/mix_v3\(/, "mix("],
78
+ [/clamp_f\(/, "clamp("],
79
+ [/smoothstep\(/, "smoothstep("],
80
+ [/fract\(/, "fract("]
81
+ ]
82
+ end
83
+
84
+ def uniform_type_to_target(type)
85
+ case type
86
+ when :float then target_float_type
87
+ when :vec2 then target_vec2_type
88
+ when :vec3 then target_vec3_type
89
+ when :vec4 then target_vec4_type
90
+ end
91
+ end
92
+
93
+ def target_float_type
94
+ "float"
95
+ end
96
+
97
+ def target_vec2_type
98
+ raise NotImplementedError
99
+ end
100
+
101
+ def target_vec3_type
102
+ raise NotImplementedError
103
+ end
104
+
105
+ def target_vec4_type
106
+ raise NotImplementedError
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class CodeGenerator
5
+ def initialize(name, uniforms, helpers_block, fragment_block)
6
+ @name = name
7
+ @uniforms = uniforms
8
+ @helpers_block = helpers_block
9
+ @fragment_block = fragment_block
10
+ end
11
+
12
+ def generate
13
+ <<~C
14
+ #include <ruby.h>
15
+ #include <math.h>
16
+ #include <stdint.h>
17
+ #ifdef __APPLE__
18
+ #include <dispatch/dispatch.h>
19
+ #endif
20
+
21
+ #{generate_types}
22
+ #{generate_math_helpers}
23
+ #{generate_uniform_struct}
24
+ #{generate_custom_helpers}
25
+ #{generate_shader_function}
26
+ #{generate_ruby_wrapper}
27
+
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
+ }
33
+ C
34
+ end
35
+
36
+ private
37
+
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
141
+ end
142
+
143
+ def generate_shader_function
144
+ shader_body = @fragment_block.call
145
+
146
+ <<~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;
227
+ }
228
+ C
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class CompiledShader
5
+ def initialize(name, ext_name, uniform_names)
6
+ @name = name
7
+ @ext_name = ext_name
8
+ @uniform_names = uniform_names
9
+ @render_method = RLSL::CompiledShaders.method("#{name}_render")
10
+ end
11
+
12
+ def metal?
13
+ false
14
+ end
15
+
16
+ def render(buffer, width, height, uniforms = {})
17
+ args = [buffer, width, height]
18
+ @uniform_names.each do |name|
19
+ args << uniforms[name]
20
+ end
21
+ @render_method.call(*args)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class FunctionContext
5
+ attr_reader :functions
6
+
7
+ def initialize
8
+ @functions = {}
9
+ end
10
+
11
+ def float(*names)
12
+ names.each { |name| @functions[name.to_sym] = { returns: :float } }
13
+ end
14
+
15
+ def vec2(*names)
16
+ names.each { |name| @functions[name.to_sym] = { returns: :vec2 } }
17
+ end
18
+
19
+ def vec3(*names)
20
+ names.each { |name| @functions[name.to_sym] = { returns: :vec3 } }
21
+ end
22
+
23
+ def vec4(*names)
24
+ names.each { |name| @functions[name.to_sym] = { returns: :vec4 } }
25
+ end
26
+
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 }
33
+ end
34
+ end
35
+ end