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.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +13 -2
- data/README.md +101 -26
- data/Rakefile +7 -0
- data/lib/rlsl/base_translator/call_parser.rb +79 -0
- data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
- data/lib/rlsl/base_translator/code_scanner.rb +140 -0
- data/lib/rlsl/base_translator.rb +182 -64
- data/lib/rlsl/code_generator/math_prelude.rb +172 -0
- data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +137 -0
- data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
- data/lib/rlsl/code_generator/template_context.rb +44 -0
- data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
- data/lib/rlsl/code_generator.rb +30 -202
- data/lib/rlsl/compiled_shader.rb +5 -13
- data/lib/rlsl/errors.rb +29 -0
- data/lib/rlsl/function_context.rb +31 -14
- data/lib/rlsl/glsl/translator.rb +35 -41
- data/lib/rlsl/msl/shader.rb +34 -46
- data/lib/rlsl/msl/translator.rb +38 -34
- data/lib/rlsl/msl/uniform_buffer_packer.rb +72 -0
- data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +137 -0
- data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +92 -0
- data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +172 -0
- data/lib/rlsl/prism/ast_visitor/scope_context.rb +48 -0
- data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
- data/lib/rlsl/prism/ast_visitor.rb +100 -286
- data/lib/rlsl/prism/builtins/function_registry.rb +131 -0
- data/lib/rlsl/prism/builtins/operator_rules.rb +123 -0
- data/lib/rlsl/prism/builtins/swizzle_rules.rb +47 -0
- data/lib/rlsl/prism/builtins.rb +39 -148
- data/lib/rlsl/prism/compilation_unit.rb +7 -0
- data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +126 -0
- data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +134 -0
- data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +108 -0
- data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +95 -0
- data/lib/rlsl/prism/emitters/base_emitter.rb +120 -414
- data/lib/rlsl/prism/emitters/c_emitter.rb +165 -112
- data/lib/rlsl/prism/emitters/glsl_emitter.rb +63 -50
- data/lib/rlsl/prism/emitters/msl_emitter.rb +67 -52
- data/lib/rlsl/prism/emitters/target_emitter.rb +124 -0
- data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
- data/lib/rlsl/prism/emitters/wgsl_emitter.rb +217 -58
- data/lib/rlsl/prism/errors.rb +9 -0
- data/lib/rlsl/prism/ir/control_flow.rb +85 -0
- data/lib/rlsl/prism/ir/definitions.rb +82 -0
- data/lib/rlsl/prism/ir/expressions.rb +201 -0
- data/lib/rlsl/prism/ir/node.rb +21 -0
- data/lib/rlsl/prism/ir/nodes.rb +4 -371
- data/lib/rlsl/prism/ir/traversal.rb +66 -0
- data/lib/rlsl/prism/mutation_analyzer.rb +30 -0
- data/lib/rlsl/prism/node_traversal.rb +41 -0
- data/lib/rlsl/prism/parameter_list.rb +46 -0
- data/lib/rlsl/prism/return_flow_validator.rb +73 -0
- data/lib/rlsl/prism/source_extractor/block_locator.rb +50 -0
- data/lib/rlsl/prism/source_extractor.rb +19 -137
- data/lib/rlsl/prism/source_unit/parser.rb +88 -0
- data/lib/rlsl/prism/source_unit.rb +42 -0
- data/lib/rlsl/prism/target_capability_validator.rb +110 -0
- data/lib/rlsl/prism/transpiler.rb +99 -59
- data/lib/rlsl/prism/type_inference/call_type_resolver.rb +59 -0
- data/lib/rlsl/prism/type_inference/call_validator.rb +75 -0
- data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +106 -0
- data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +147 -0
- data/lib/rlsl/prism/type_inference/definition_inferer.rb +56 -0
- data/lib/rlsl/prism/type_inference/expression_inferer.rb +96 -0
- data/lib/rlsl/prism/type_inference/field_type_resolver.rb +38 -0
- data/lib/rlsl/prism/type_inference/inferer_registry.rb +38 -0
- data/lib/rlsl/prism/type_inference/scope_stack.rb +47 -0
- data/lib/rlsl/prism/type_inference/type_environment.rb +112 -0
- data/lib/rlsl/prism/type_inference/type_shapes.rb +33 -0
- data/lib/rlsl/prism/type_inference.rb +120 -249
- data/lib/rlsl/runtime_shader.rb +47 -0
- data/lib/rlsl/shader_builder/build_service.rb +104 -0
- data/lib/rlsl/shader_builder/native_extension_compiler.rb +97 -0
- data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
- data/lib/rlsl/shader_builder/source_resolver.rb +109 -0
- data/lib/rlsl/shader_builder.rb +60 -111
- data/lib/rlsl/shader_name.rb +18 -0
- data/lib/rlsl/types/catalog.rb +47 -0
- data/lib/rlsl/types/target_resolver.rb +15 -0
- data/lib/rlsl/types/type_spec.rb +167 -0
- data/lib/rlsl/types/value_normalizer.rb +86 -0
- data/lib/rlsl/types.rb +9 -31
- data/lib/rlsl/uniform_context.rb +22 -11
- data/lib/rlsl/version.rb +1 -1
- data/lib/rlsl/wgsl/translator.rb +46 -39
- data/lib/rlsl/wgsl/uniform_layout.rb +25 -0
- data/lib/rlsl.rb +38 -15
- metadata +76 -11
|
@@ -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 { unsigned char _unused; } 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
|
data/lib/rlsl/code_generator.rb
CHANGED
|
@@ -1,229 +1,57 @@
|
|
|
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
|
-
def initialize(name, uniforms, helpers_block, fragment_block)
|
|
6
|
-
@
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
def initialize(name, uniforms, helpers_block, fragment_block, extension_name: name)
|
|
12
|
+
@context = TemplateContext.new(
|
|
13
|
+
name: name,
|
|
14
|
+
uniforms: uniforms,
|
|
15
|
+
helpers_block: helpers_block,
|
|
16
|
+
fragment_block: fragment_block,
|
|
17
|
+
extension_name: extension_name
|
|
18
|
+
)
|
|
10
19
|
end
|
|
11
20
|
|
|
12
21
|
def generate
|
|
13
22
|
<<~C
|
|
14
23
|
#include <ruby.h>
|
|
24
|
+
#include <ruby/thread.h>
|
|
25
|
+
#include <limits.h>
|
|
15
26
|
#include <math.h>
|
|
16
27
|
#include <stdint.h>
|
|
17
28
|
#ifdef __APPLE__
|
|
18
29
|
#include <dispatch/dispatch.h>
|
|
19
30
|
#endif
|
|
20
31
|
|
|
21
|
-
#{
|
|
22
|
-
#{
|
|
23
|
-
#{
|
|
24
|
-
#{
|
|
25
|
-
#{
|
|
26
|
-
#{
|
|
32
|
+
#{RLSL::C_TYPES}
|
|
33
|
+
#{MathPrelude.code}
|
|
34
|
+
#{UniformStructGenerator.new(@context).generate}
|
|
35
|
+
#{custom_helpers}
|
|
36
|
+
#{ShaderFunctionGenerator.new(@context).generate}
|
|
37
|
+
#{RubyWrapperGenerator.new(@context).generate}
|
|
27
38
|
|
|
28
|
-
|
|
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
|
-
}
|
|
39
|
+
#{init_function}
|
|
33
40
|
C
|
|
34
41
|
end
|
|
35
42
|
|
|
36
43
|
private
|
|
37
44
|
|
|
38
|
-
def
|
|
39
|
-
|
|
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
|
|
45
|
+
def custom_helpers
|
|
46
|
+
@context.helpers_code
|
|
141
47
|
end
|
|
142
48
|
|
|
143
|
-
def
|
|
144
|
-
shader_body = @fragment_block.call
|
|
145
|
-
|
|
49
|
+
def init_function
|
|
146
50
|
<<~C
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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;
|
|
51
|
+
void Init_#{@context.extension_name}(void) {
|
|
52
|
+
VALUE mRLSL = rb_define_module("RLSL");
|
|
53
|
+
VALUE mShaders = rb_define_module_under(mRLSL, "CompiledShaders");
|
|
54
|
+
rb_define_module_function(mShaders, "#{@context.extension_name}_render", shader_#{@context.name}_render, #{@context.render_arity});
|
|
227
55
|
}
|
|
228
56
|
C
|
|
229
57
|
end
|
data/lib/rlsl/compiled_shader.rb
CHANGED
|
@@ -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,
|
|
6
|
-
|
|
4
|
+
class CompiledShader < RuntimeShader
|
|
5
|
+
def initialize(name, ext_name, uniforms)
|
|
6
|
+
super(name, uniforms)
|
|
7
7
|
@ext_name = ext_name
|
|
8
|
-
@
|
|
9
|
-
@render_method = RLSL::CompiledShaders.method("#{name}_render")
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def metal?
|
|
13
|
-
false
|
|
8
|
+
@render_method = RLSL::CompiledShaders.method("#{ext_name}_render")
|
|
14
9
|
end
|
|
15
10
|
|
|
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
|
data/lib/rlsl/errors.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
SourceLocation = Struct.new(:source_name, :line, :column, keyword_init: true) do
|
|
5
|
+
def to_s
|
|
6
|
+
"#{source_name || '(shader source)'}:#{line}:#{column}"
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class Error < StandardError
|
|
11
|
+
attr_reader :source_location
|
|
12
|
+
|
|
13
|
+
def with_source_location(location)
|
|
14
|
+
@source_location ||= location
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def message
|
|
19
|
+
return super unless source_location
|
|
20
|
+
|
|
21
|
+
"#{super} at #{source_location}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
class InternalError < Error; end
|
|
25
|
+
class ParseError < Error; end
|
|
26
|
+
class TranslationError < Error; end
|
|
27
|
+
class CompilationError < Error; end
|
|
28
|
+
class UniformValueError < ArgumentError; end
|
|
29
|
+
end
|
|
@@ -8,28 +8,45 @@ module RLSL
|
|
|
8
8
|
@functions = {}
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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
|
|
24
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
data/lib/rlsl/glsl/translator.rb
CHANGED
|
@@ -3,37 +3,30 @@
|
|
|
3
3
|
module RLSL
|
|
4
4
|
module GLSL
|
|
5
5
|
class Translator < BaseTranslator
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
+
).merge(
|
|
14
|
+
"fmodf" => BaseTranslator.rename_call("mod"),
|
|
15
|
+
"atan2f" => BaseTranslator.rename_call("atan")
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def initialize(uniforms, helpers_code, fragment_code, version: "450", name: nil)
|
|
20
|
+
super(uniforms, helpers_code, fragment_code, name: name)
|
|
21
|
+
@version = validate_version!(version)
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
protected
|
|
24
25
|
|
|
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
26
|
def generate_shader(helpers, fragment)
|
|
35
27
|
<<~GLSL
|
|
36
28
|
#version #{@version}
|
|
29
|
+
#{generated_by_comment("GLSL")}
|
|
37
30
|
|
|
38
31
|
// Uniforms
|
|
39
32
|
#{generate_uniform_declarations}
|
|
@@ -44,8 +37,7 @@ module RLSL
|
|
|
44
37
|
#{helpers}
|
|
45
38
|
|
|
46
39
|
vec3 shader_fragment(vec2 frag_coord, vec2 resolution) {
|
|
47
|
-
|
|
48
|
-
#{fragment}
|
|
40
|
+
#{indent_source(fragment, 4)}
|
|
49
41
|
}
|
|
50
42
|
|
|
51
43
|
layout(local_size_x = 8, local_size_y = 8) in;
|
|
@@ -70,27 +62,29 @@ module RLSL
|
|
|
70
62
|
|
|
71
63
|
private
|
|
72
64
|
|
|
65
|
+
def profile
|
|
66
|
+
PROFILE
|
|
67
|
+
end
|
|
68
|
+
|
|
73
69
|
def generate_uniform_declarations
|
|
74
|
-
declarations = ["layout(binding = 1) uniform ShaderUniforms {"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
declarations = ["layout(std140, binding = 1) uniform ShaderUniforms {"]
|
|
71
|
+
declarations.concat(
|
|
72
|
+
uniform_lines(resolution_line: " #{target_vec2_type} resolution;") do |name, glsl_type|
|
|
73
|
+
" #{glsl_type} #{name};"
|
|
74
|
+
end
|
|
75
|
+
)
|
|
80
76
|
declarations << "} u;"
|
|
77
|
+
texture_uniforms.each_with_index do |(name, _type), index|
|
|
78
|
+
declarations << "layout(binding = #{index + 2}) uniform sampler2D #{name};"
|
|
79
|
+
end
|
|
81
80
|
declarations.join("\n")
|
|
82
81
|
end
|
|
83
82
|
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def target_vec3_type
|
|
89
|
-
"vec3"
|
|
90
|
-
end
|
|
83
|
+
def validate_version!(version)
|
|
84
|
+
normalized = version.to_s
|
|
85
|
+
return normalized if normalized.match?(/\A[1-9]\d{2}(?: (?:core|compatibility|es))?\z/)
|
|
91
86
|
|
|
92
|
-
|
|
93
|
-
"vec4"
|
|
87
|
+
raise ArgumentError, "Invalid GLSL version: #{version.inspect}"
|
|
94
88
|
end
|
|
95
89
|
end
|
|
96
90
|
end
|
data/lib/rlsl/msl/shader.rb
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
begin
|
|
4
|
-
require "metaco"
|
|
5
|
-
METACO_AVAILABLE = true
|
|
6
|
-
rescue LoadError
|
|
7
|
-
METACO_AVAILABLE = false
|
|
8
|
-
end
|
|
9
|
-
|
|
10
3
|
module RLSL
|
|
11
4
|
module MSL
|
|
12
|
-
|
|
5
|
+
begin
|
|
6
|
+
require "metaco"
|
|
7
|
+
METACO_AVAILABLE = true
|
|
8
|
+
rescue LoadError
|
|
9
|
+
METACO_AVAILABLE = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Shader < RuntimeShader
|
|
13
|
+
COMPILED_HANDLE_CACHE_LIMIT = 64
|
|
13
14
|
attr_reader :name, :msl_source
|
|
14
15
|
|
|
15
16
|
def initialize(name, uniforms, msl_source)
|
|
16
|
-
|
|
17
|
-
@uniform_types = uniforms
|
|
18
|
-
@uniform_names = uniforms.keys
|
|
17
|
+
super(name, uniforms)
|
|
19
18
|
@msl_source = msl_source
|
|
20
19
|
@compiled_handles = {}
|
|
20
|
+
value_uniform_types = @uniform_types.reject { |_name, type| type == :sampler2D }
|
|
21
|
+
@uniform_buffer_packer = UniformBufferPacker.new(@name, value_uniform_types, value_uniform_types.keys)
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def metal?
|
|
24
25
|
true
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
def
|
|
28
|
+
def render(handle, width, height, uniforms = {})
|
|
29
|
+
render_metal(handle, width, height, uniforms)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def prepare(handle)
|
|
28
33
|
unless METACO_AVAILABLE
|
|
29
34
|
raise LoadError, "metaco gem is required for Metal rendering. Install it with: gem install metaco"
|
|
30
35
|
end
|
|
@@ -32,10 +37,26 @@ module RLSL
|
|
|
32
37
|
unless @compiled_handles[handle]
|
|
33
38
|
Metaco.compile_compute_shader(handle, @msl_source)
|
|
34
39
|
@compiled_handles[handle] = true
|
|
40
|
+
@compiled_handles.shift while @compiled_handles.length > COMPILED_HANDLE_CACHE_LIMIT
|
|
35
41
|
end
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def render_metal(handle, width, height, uniforms = {}, textures: {})
|
|
46
|
+
prepare(handle)
|
|
36
47
|
|
|
37
48
|
uniform_data = pack_uniforms(uniforms, width, height)
|
|
38
49
|
|
|
50
|
+
sampler_names = @uniform_types.filter_map { |name, type| name if type == :sampler2D }
|
|
51
|
+
sampler_names.each do |name|
|
|
52
|
+
texture = textures[name] || textures[name.to_s]
|
|
53
|
+
raise ArgumentError, "missing texture uniform: #{name}" unless texture
|
|
54
|
+
unless Metaco.respond_to?(:bind_compute_texture)
|
|
55
|
+
raise LoadError, "metaco texture support is required for sampler2D uniforms"
|
|
56
|
+
end
|
|
57
|
+
Metaco.bind_compute_texture(handle, sampler_names.index(name), texture)
|
|
58
|
+
end
|
|
59
|
+
|
|
39
60
|
Metaco.dispatch_compute(handle, uniform_data)
|
|
40
61
|
Metaco.present_compute(handle)
|
|
41
62
|
end
|
|
@@ -43,40 +64,7 @@ module RLSL
|
|
|
43
64
|
private
|
|
44
65
|
|
|
45
66
|
def pack_uniforms(uniforms, width, height)
|
|
46
|
-
|
|
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")
|
|
67
|
+
@uniform_buffer_packer.pack(width, height, uniforms)
|
|
80
68
|
end
|
|
81
69
|
end
|
|
82
70
|
end
|