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
data/lib/rlsl/base_translator.rb
CHANGED
|
@@ -1,92 +1,77 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "base_translator/code_rewriter"
|
|
4
|
+
|
|
3
5
|
module RLSL
|
|
4
6
|
class BaseTranslator
|
|
5
|
-
|
|
7
|
+
SourceSnippet = Struct.new(:code, :format, keyword_init: true) do
|
|
8
|
+
def target_code?
|
|
9
|
+
format == :target
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
TargetProfile = Struct.new(
|
|
14
|
+
:uniform_target,
|
|
15
|
+
:identifier_replacements,
|
|
16
|
+
:call_rewrites,
|
|
17
|
+
:removed_identifiers,
|
|
18
|
+
keyword_init: true
|
|
19
|
+
) do
|
|
20
|
+
def translate(code)
|
|
21
|
+
CodeRewriter.new(code).rewrite(
|
|
22
|
+
identifier_replacements: identifier_replacements,
|
|
23
|
+
call_rewrites: call_rewrites,
|
|
24
|
+
removed_identifiers: removed_identifiers
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
6
28
|
|
|
7
|
-
|
|
29
|
+
REMOVED_IDENTIFIERS = %w[static inline].freeze
|
|
8
30
|
|
|
9
|
-
def initialize(uniforms, helpers_code, fragment_code)
|
|
31
|
+
def initialize(uniforms, helpers_code, fragment_code, name: nil)
|
|
10
32
|
@uniforms = uniforms
|
|
11
|
-
@
|
|
12
|
-
@
|
|
33
|
+
@shader_name = name&.to_s
|
|
34
|
+
@helpers_source = normalize_source(helpers_code)
|
|
35
|
+
@fragment_source = normalize_source(fragment_code)
|
|
13
36
|
end
|
|
14
37
|
|
|
15
38
|
def translate
|
|
16
|
-
helpers_translated = translate_code(@
|
|
17
|
-
fragment_translated = translate_code(@
|
|
39
|
+
helpers_translated = translate_code(@helpers_source)
|
|
40
|
+
fragment_translated = translate_code(@fragment_source)
|
|
18
41
|
generate_shader(helpers_translated, fragment_translated)
|
|
19
42
|
end
|
|
20
43
|
|
|
21
44
|
protected
|
|
22
45
|
|
|
23
|
-
def translate_code(
|
|
24
|
-
|
|
46
|
+
def translate_code(source)
|
|
47
|
+
snippet = normalize_source(source)
|
|
48
|
+
return "" if snippet.code.empty?
|
|
49
|
+
validate_source_format!(snippet)
|
|
50
|
+
return snippet.code if snippet.target_code?
|
|
25
51
|
|
|
26
|
-
|
|
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
|
|
52
|
+
profile.translate(snippet.code)
|
|
37
53
|
end
|
|
38
54
|
|
|
55
|
+
def validate_source_format!(_source); end
|
|
56
|
+
|
|
39
57
|
def generate_shader(_helpers, _fragment)
|
|
40
58
|
raise NotImplementedError, "Subclasses must implement generate_shader"
|
|
41
59
|
end
|
|
42
60
|
|
|
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
61
|
def uniform_type_to_target(type)
|
|
85
62
|
case type
|
|
86
63
|
when :float then target_float_type
|
|
64
|
+
when :int then target_int_type
|
|
65
|
+
when :bool then target_bool_type
|
|
87
66
|
when :vec2 then target_vec2_type
|
|
88
67
|
when :vec3 then target_vec3_type
|
|
89
68
|
when :vec4 then target_vec4_type
|
|
69
|
+
when :mat2 then target_mat2_type
|
|
70
|
+
when :mat3 then target_mat3_type
|
|
71
|
+
when :mat4 then target_mat4_type
|
|
72
|
+
when :sampler2D then target_sampler2d_type
|
|
73
|
+
else
|
|
74
|
+
raise ArgumentError, "Unsupported uniform type: #{type.inspect}"
|
|
90
75
|
end
|
|
91
76
|
end
|
|
92
77
|
|
|
@@ -94,16 +79,149 @@ module RLSL
|
|
|
94
79
|
"float"
|
|
95
80
|
end
|
|
96
81
|
|
|
82
|
+
def target_int_type
|
|
83
|
+
UniformTypes.target_type(:int, uniform_target)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def target_bool_type
|
|
87
|
+
UniformTypes.target_type(:bool, uniform_target)
|
|
88
|
+
end
|
|
89
|
+
|
|
97
90
|
def target_vec2_type
|
|
98
|
-
|
|
91
|
+
UniformTypes.target_type(:vec2, uniform_target)
|
|
99
92
|
end
|
|
100
93
|
|
|
101
94
|
def target_vec3_type
|
|
102
|
-
|
|
95
|
+
UniformTypes.target_type(:vec3, uniform_target)
|
|
103
96
|
end
|
|
104
97
|
|
|
105
98
|
def target_vec4_type
|
|
106
|
-
|
|
99
|
+
UniformTypes.target_type(:vec4, uniform_target)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def target_mat2_type
|
|
103
|
+
UniformTypes.target_type(:mat2, uniform_target)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def target_mat3_type
|
|
107
|
+
UniformTypes.target_type(:mat3, uniform_target)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def target_mat4_type
|
|
111
|
+
UniformTypes.target_type(:mat4, uniform_target)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def target_sampler2d_type
|
|
115
|
+
UniformTypes.target_type(:sampler2D, uniform_target)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def uniform_target
|
|
119
|
+
profile.uniform_target
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def uniform_lines(resolution_line:, &block)
|
|
123
|
+
lines = [resolution_line]
|
|
124
|
+
value_uniforms.each do |name, type|
|
|
125
|
+
lines << block.call(name, uniform_type_to_target(type))
|
|
126
|
+
end
|
|
127
|
+
lines
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def value_uniforms
|
|
131
|
+
@uniforms.reject { |_name, type| type.to_sym == :sampler2D }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def texture_uniforms
|
|
135
|
+
@uniforms.select { |_name, type| type.to_sym == :sampler2D }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def indent_source(source, spaces)
|
|
139
|
+
prefix = " " * spaces
|
|
140
|
+
source.to_s.lines.map { |line| line.strip.empty? ? line : "#{prefix}#{line}" }.join.rstrip
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def generated_by_comment(language)
|
|
144
|
+
suffix = @shader_name ? ": #{@shader_name}" : ""
|
|
145
|
+
"// #{language} shader generated by RLSL#{suffix}"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def profile
|
|
149
|
+
return self.class::PROFILE if self.class.const_defined?(:PROFILE, false)
|
|
150
|
+
|
|
151
|
+
raise NotImplementedError, "Subclasses must define PROFILE"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.build_profile(uniform_target:, identifier_replacements:, call_rewrites:, removed_identifiers: REMOVED_IDENTIFIERS)
|
|
155
|
+
TargetProfile.new(
|
|
156
|
+
uniform_target: uniform_target,
|
|
157
|
+
identifier_replacements: identifier_replacements.freeze,
|
|
158
|
+
call_rewrites: call_rewrites.freeze,
|
|
159
|
+
removed_identifiers: removed_identifiers.freeze
|
|
160
|
+
).freeze
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def self.common_call_rewrites(target_vec2:, target_vec3:, target_vec4:)
|
|
164
|
+
{
|
|
165
|
+
"vec2_new" => rename_call(target_vec2),
|
|
166
|
+
"vec3_new" => rename_call(target_vec3),
|
|
167
|
+
"vec4_new" => rename_call(target_vec4),
|
|
168
|
+
"vec2_add" => infix_call("+"),
|
|
169
|
+
"vec3_add" => infix_call("+"),
|
|
170
|
+
"vec2_sub" => infix_call("-"),
|
|
171
|
+
"vec3_sub" => infix_call("-"),
|
|
172
|
+
"vec2_mul" => infix_call("*"),
|
|
173
|
+
"vec3_mul" => infix_call("*"),
|
|
174
|
+
"vec2_div" => infix_call("/"),
|
|
175
|
+
"vec3_div" => infix_call("/"),
|
|
176
|
+
"vec2_dot" => rename_call("dot"),
|
|
177
|
+
"vec3_dot" => rename_call("dot"),
|
|
178
|
+
"vec2_length" => rename_call("length"),
|
|
179
|
+
"vec3_length" => rename_call("length"),
|
|
180
|
+
"vec2_normalize" => rename_call("normalize"),
|
|
181
|
+
"vec3_normalize" => rename_call("normalize"),
|
|
182
|
+
"sqrtf" => rename_call("sqrt"),
|
|
183
|
+
"sinf" => rename_call("sin"),
|
|
184
|
+
"cosf" => rename_call("cos"),
|
|
185
|
+
"tanf" => rename_call("tan"),
|
|
186
|
+
"fabsf" => rename_call("abs"),
|
|
187
|
+
"fminf" => rename_call("min"),
|
|
188
|
+
"fmaxf" => rename_call("max"),
|
|
189
|
+
"floorf" => rename_call("floor"),
|
|
190
|
+
"ceilf" => rename_call("ceil"),
|
|
191
|
+
"powf" => rename_call("pow"),
|
|
192
|
+
"expf" => rename_call("exp"),
|
|
193
|
+
"logf" => rename_call("log"),
|
|
194
|
+
"atan2f" => rename_call("atan2"),
|
|
195
|
+
"fmodf" => rename_call("fmod"),
|
|
196
|
+
"mix_f" => rename_call("mix"),
|
|
197
|
+
"mix_v3" => rename_call("mix"),
|
|
198
|
+
"clamp_f" => rename_call("clamp"),
|
|
199
|
+
"smoothstep" => rename_call("smoothstep"),
|
|
200
|
+
"fract" => rename_call("fract")
|
|
201
|
+
}.freeze
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def self.rename_call(name)
|
|
205
|
+
lambda do |arguments|
|
|
206
|
+
"#{name}(#{arguments.join(', ')})"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def self.infix_call(operator)
|
|
211
|
+
lambda do |arguments|
|
|
212
|
+
raise ArgumentError, "Expected 2 arguments for #{operator} rewrite, got #{arguments.length}" unless arguments.length == 2
|
|
213
|
+
|
|
214
|
+
"(#{arguments[0]} #{operator} #{arguments[1]})"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
private
|
|
219
|
+
|
|
220
|
+
def normalize_source(source)
|
|
221
|
+
return SourceSnippet.new(code: "", format: :legacy) if source.nil?
|
|
222
|
+
return source if source.is_a?(SourceSnippet)
|
|
223
|
+
|
|
224
|
+
SourceSnippet.new(code: source.to_s, format: :legacy)
|
|
107
225
|
end
|
|
108
226
|
end
|
|
109
227
|
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
class CodeGenerator
|
|
5
|
+
module MathPrelude
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def code
|
|
9
|
+
<<~C
|
|
10
|
+
static inline vec2 vec2_new(float x, float y) { return (vec2){x, y}; }
|
|
11
|
+
static inline vec3 vec3_new(float x, float y, float z) { return (vec3){x, y, z}; }
|
|
12
|
+
static inline vec4 vec4_new(float x, float y, float z, float w) { return (vec4){x, y, z, w}; }
|
|
13
|
+
static inline vec2 vec2_splat(float x) { return vec2_new(x, x); }
|
|
14
|
+
static inline vec3 vec3_splat(float x) { return vec3_new(x, x, x); }
|
|
15
|
+
static inline vec4 vec4_splat(float x) { return vec4_new(x, x, x, x); }
|
|
16
|
+
|
|
17
|
+
static inline vec2 vec2_from_vec2(vec2 v) { return v; }
|
|
18
|
+
static inline vec3 vec3_from_vec3(vec3 v) { return v; }
|
|
19
|
+
static inline vec4 vec4_from_vec4(vec4 v) { return v; }
|
|
20
|
+
static inline vec3 vec3_from_vec2_float(vec2 v, float z) { return vec3_new(v.x, v.y, z); }
|
|
21
|
+
static inline vec3 vec3_from_float_vec2(float x, vec2 v) { return vec3_new(x, v.x, v.y); }
|
|
22
|
+
static inline vec4 vec4_from_vec3_float(vec3 v, float w) { return vec4_new(v.x, v.y, v.z, w); }
|
|
23
|
+
static inline vec4 vec4_from_float_vec3(float x, vec3 v) { return vec4_new(x, v.x, v.y, v.z); }
|
|
24
|
+
static inline vec4 vec4_from_vec2_vec2(vec2 a, vec2 b) { return vec4_new(a.x, a.y, b.x, b.y); }
|
|
25
|
+
static inline vec4 vec4_from_vec2_float_float(vec2 v, float z, float w) { return vec4_new(v.x, v.y, z, w); }
|
|
26
|
+
static inline vec4 vec4_from_float_vec2_float(float x, vec2 v, float w) { return vec4_new(x, v.x, v.y, w); }
|
|
27
|
+
static inline vec4 vec4_from_float_float_vec2(float x, float y, vec2 v) { return vec4_new(x, y, v.x, v.y); }
|
|
28
|
+
|
|
29
|
+
static inline float vec2_component(vec2 v, int i) { return i == 0 ? v.x : v.y; }
|
|
30
|
+
static inline float vec3_component(vec3 v, int i) { return i == 0 ? v.x : (i == 1 ? v.y : v.z); }
|
|
31
|
+
static inline float vec4_component(vec4 v, int i) {
|
|
32
|
+
return i == 0 ? v.x : (i == 1 ? v.y : (i == 2 ? v.z : v.w));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#define RLSL_DEFINE_SWIZZLES(type) \
|
|
36
|
+
static inline vec2 type##_swizzle2(type v, int a, int b) { \
|
|
37
|
+
return vec2_new(type##_component(v, a), type##_component(v, b)); \
|
|
38
|
+
} \
|
|
39
|
+
static inline vec3 type##_swizzle3(type v, int a, int b, int c) { \
|
|
40
|
+
return vec3_new(type##_component(v, a), type##_component(v, b), type##_component(v, c)); \
|
|
41
|
+
} \
|
|
42
|
+
static inline vec4 type##_swizzle4(type v, int a, int b, int c, int d) { \
|
|
43
|
+
return vec4_new(type##_component(v, a), type##_component(v, b), type##_component(v, c), type##_component(v, d)); \
|
|
44
|
+
}
|
|
45
|
+
RLSL_DEFINE_SWIZZLES(vec2)
|
|
46
|
+
RLSL_DEFINE_SWIZZLES(vec3)
|
|
47
|
+
RLSL_DEFINE_SWIZZLES(vec4)
|
|
48
|
+
#undef RLSL_DEFINE_SWIZZLES
|
|
49
|
+
|
|
50
|
+
static inline vec2 vec2_add(vec2 a, vec2 b) { return (vec2){a.x + b.x, a.y + b.y}; }
|
|
51
|
+
static inline vec3 vec3_add(vec3 a, vec3 b) { return (vec3){a.x + b.x, a.y + b.y, a.z + b.z}; }
|
|
52
|
+
static inline vec4 vec4_add(vec4 a, vec4 b) { return (vec4){a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; }
|
|
53
|
+
|
|
54
|
+
static inline vec2 vec2_sub(vec2 a, vec2 b) { return (vec2){a.x - b.x, a.y - b.y}; }
|
|
55
|
+
static inline vec3 vec3_sub(vec3 a, vec3 b) { return (vec3){a.x - b.x, a.y - b.y, a.z - b.z}; }
|
|
56
|
+
static inline vec4 vec4_sub(vec4 a, vec4 b) { return (vec4){a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w}; }
|
|
57
|
+
|
|
58
|
+
static inline vec2 vec2_add_scalar(vec2 a, float s) { return (vec2){a.x + s, a.y + s}; }
|
|
59
|
+
static inline vec3 vec3_add_scalar(vec3 a, float s) { return (vec3){a.x + s, a.y + s, a.z + s}; }
|
|
60
|
+
static inline vec4 vec4_add_scalar(vec4 a, float s) { return (vec4){a.x + s, a.y + s, a.z + s, a.w + s}; }
|
|
61
|
+
static inline vec2 vec2_scalar_add(float s, vec2 a) { return vec2_add_scalar(a, s); }
|
|
62
|
+
static inline vec3 vec3_scalar_add(float s, vec3 a) { return vec3_add_scalar(a, s); }
|
|
63
|
+
static inline vec4 vec4_scalar_add(float s, vec4 a) { return vec4_add_scalar(a, s); }
|
|
64
|
+
|
|
65
|
+
static inline vec2 vec2_sub_scalar(vec2 a, float s) { return (vec2){a.x - s, a.y - s}; }
|
|
66
|
+
static inline vec3 vec3_sub_scalar(vec3 a, float s) { return (vec3){a.x - s, a.y - s, a.z - s}; }
|
|
67
|
+
static inline vec4 vec4_sub_scalar(vec4 a, float s) { return (vec4){a.x - s, a.y - s, a.z - s, a.w - s}; }
|
|
68
|
+
static inline vec2 vec2_scalar_sub(float s, vec2 a) { return (vec2){s - a.x, s - a.y}; }
|
|
69
|
+
static inline vec3 vec3_scalar_sub(float s, vec3 a) { return (vec3){s - a.x, s - a.y, s - a.z}; }
|
|
70
|
+
static inline vec4 vec4_scalar_sub(float s, vec4 a) { return (vec4){s - a.x, s - a.y, s - a.z, s - a.w}; }
|
|
71
|
+
|
|
72
|
+
static inline vec2 vec2_mul_scalar(vec2 a, float s) { return (vec2){a.x * s, a.y * s}; }
|
|
73
|
+
static inline vec3 vec3_mul_scalar(vec3 a, float s) { return (vec3){a.x * s, a.y * s, a.z * s}; }
|
|
74
|
+
static inline vec4 vec4_mul_scalar(vec4 a, float s) { return (vec4){a.x * s, a.y * s, a.z * s, a.w * s}; }
|
|
75
|
+
static inline vec2 vec2_scalar_mul(float s, vec2 a) { return vec2_mul_scalar(a, s); }
|
|
76
|
+
static inline vec3 vec3_scalar_mul(float s, vec3 a) { return vec3_mul_scalar(a, s); }
|
|
77
|
+
static inline vec4 vec4_scalar_mul(float s, vec4 a) { return vec4_mul_scalar(a, s); }
|
|
78
|
+
static inline vec2 vec2_mul_components(vec2 a, vec2 b) { return (vec2){a.x * b.x, a.y * b.y}; }
|
|
79
|
+
static inline vec3 vec3_mul_components(vec3 a, vec3 b) { return (vec3){a.x * b.x, a.y * b.y, a.z * b.z}; }
|
|
80
|
+
static inline vec4 vec4_mul_components(vec4 a, vec4 b) { return (vec4){a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w}; }
|
|
81
|
+
|
|
82
|
+
static inline vec2 vec2_div_scalar(vec2 a, float s) { return (vec2){a.x / s, a.y / s}; }
|
|
83
|
+
static inline vec3 vec3_div_scalar(vec3 a, float s) { return (vec3){a.x / s, a.y / s, a.z / s}; }
|
|
84
|
+
static inline vec4 vec4_div_scalar(vec4 a, float s) { return (vec4){a.x / s, a.y / s, a.z / s, a.w / s}; }
|
|
85
|
+
static inline vec2 vec2_scalar_div(float s, vec2 a) { return (vec2){s / a.x, s / a.y}; }
|
|
86
|
+
static inline vec3 vec3_scalar_div(float s, vec3 a) { return (vec3){s / a.x, s / a.y, s / a.z}; }
|
|
87
|
+
static inline vec4 vec4_scalar_div(float s, vec4 a) { return (vec4){s / a.x, s / a.y, s / a.z, s / a.w}; }
|
|
88
|
+
static inline vec2 vec2_div_components(vec2 a, vec2 b) { return (vec2){a.x / b.x, a.y / b.y}; }
|
|
89
|
+
static inline vec3 vec3_div_components(vec3 a, vec3 b) { return (vec3){a.x / b.x, a.y / b.y, a.z / b.z}; }
|
|
90
|
+
static inline vec4 vec4_div_components(vec4 a, vec4 b) { return (vec4){a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w}; }
|
|
91
|
+
|
|
92
|
+
/* Legacy names used by raw C fragments. */
|
|
93
|
+
static inline vec2 vec2_mul(vec2 a, float s) { return vec2_mul_scalar(a, s); }
|
|
94
|
+
static inline vec3 vec3_mul(vec3 a, float s) { return vec3_mul_scalar(a, s); }
|
|
95
|
+
static inline vec2 vec2_div(vec2 a, float s) { return vec2_div_scalar(a, s); }
|
|
96
|
+
static inline vec3 vec3_div(vec3 a, float s) { return vec3_div_scalar(a, s); }
|
|
97
|
+
|
|
98
|
+
static inline float vec2_dot(vec2 a, vec2 b) { return a.x * b.x + a.y * b.y; }
|
|
99
|
+
static inline float vec3_dot(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
|
|
100
|
+
static inline float vec4_dot(vec4 a, vec4 b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; }
|
|
101
|
+
|
|
102
|
+
static inline vec3 vec3_cross(vec3 a, vec3 b) {
|
|
103
|
+
return vec3_new(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static inline float vec2_length(vec2 v) { return sqrtf(v.x * v.x + v.y * v.y); }
|
|
107
|
+
static inline float vec3_length(vec3 v) { return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); }
|
|
108
|
+
static inline float vec4_length(vec4 v) { return sqrtf(vec4_dot(v, v)); }
|
|
109
|
+
|
|
110
|
+
static inline float vec2_distance(vec2 a, vec2 b) { return vec2_length(vec2_sub(a, b)); }
|
|
111
|
+
static inline float vec3_distance(vec3 a, vec3 b) { return vec3_length(vec3_sub(a, b)); }
|
|
112
|
+
static inline float vec4_distance(vec4 a, vec4 b) { return vec4_length(vec4_sub(a, b)); }
|
|
113
|
+
|
|
114
|
+
static inline vec2 vec2_normalize(vec2 v) { float l = vec2_length(v); return l > 0 ? vec2_div_scalar(v, l) : v; }
|
|
115
|
+
static inline vec3 vec3_normalize(vec3 v) { float l = vec3_length(v); return l > 0 ? vec3_div_scalar(v, l) : v; }
|
|
116
|
+
static inline vec4 vec4_normalize(vec4 v) { float l = vec4_length(v); return l > 0 ? vec4_div_scalar(v, l) : v; }
|
|
117
|
+
|
|
118
|
+
static inline float fract(float x) { return x - floorf(x); }
|
|
119
|
+
static inline float rlsl_mod(float x, float y) { return x - y * floorf(x / y); }
|
|
120
|
+
static inline float sign_f(float x) { return (x > 0.0f) - (x < 0.0f); }
|
|
121
|
+
static inline float step_f(float edge, float x) { return x < edge ? 0.0f : 1.0f; }
|
|
122
|
+
static inline float mix_f(float a, float b, float t) { return a + (b - a) * t; }
|
|
123
|
+
static inline vec2 mix_v2(vec2 a, vec2 b, float t) {
|
|
124
|
+
return (vec2){a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t};
|
|
125
|
+
}
|
|
126
|
+
static inline vec3 mix_v3(vec3 a, vec3 b, float t) {
|
|
127
|
+
return (vec3){a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t};
|
|
128
|
+
}
|
|
129
|
+
static inline vec4 mix_v4(vec4 a, vec4 b, float t) {
|
|
130
|
+
return (vec4){a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static inline float clamp_f(float x, float lo, float hi) {
|
|
134
|
+
return x < lo ? lo : (x > hi ? hi : x);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static inline float smoothstep(float edge0, float edge1, float x) {
|
|
138
|
+
float t = clamp_f((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
|
|
139
|
+
return t * t * (3.0f - 2.0f * t);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static inline float hash21(vec2 p) {
|
|
143
|
+
float dot = p.x * 12.9898f + p.y * 78.233f;
|
|
144
|
+
return fract(sinf(dot) * 43758.5453f);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static inline vec2 hash22(vec2 p) {
|
|
148
|
+
float x = sinf(vec2_dot(p, vec2_new(127.1f, 311.7f)));
|
|
149
|
+
float y = sinf(vec2_dot(p, vec2_new(269.5f, 183.3f)));
|
|
150
|
+
return vec2_new(fract(x * 43758.5453f), fract(y * 43758.5453f));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static inline vec3 reflect(vec3 I, vec3 N) {
|
|
154
|
+
float d = vec3_dot(N, I);
|
|
155
|
+
return vec3_new(I.x - 2.0f * d * N.x, I.y - 2.0f * d * N.y, I.z - 2.0f * d * N.z);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static inline vec3 refract(vec3 I, vec3 N, float eta) {
|
|
159
|
+
float d = vec3_dot(N, I);
|
|
160
|
+
float k = 1.0f - eta * eta * (1.0f - d * d);
|
|
161
|
+
if (k < 0.0f) {
|
|
162
|
+
return vec3_new(0.0f, 0.0f, 0.0f);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
float s = eta * d + sqrtf(k);
|
|
166
|
+
return vec3_new(eta * I.x - s * N.x, eta * I.y - s * N.y, eta * I.z - s * N.z);
|
|
167
|
+
}
|
|
168
|
+
C
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
size_t idx = ((size_t)y * (size_t)width + (size_t)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
|
+
typedef struct {
|
|
33
|
+
uint8_t *pixels;
|
|
34
|
+
int width;
|
|
35
|
+
int height;
|
|
36
|
+
vec2 resolution;
|
|
37
|
+
Uniforms uniforms;
|
|
38
|
+
} render_arguments;
|
|
39
|
+
|
|
40
|
+
static void *render_without_gvl(void *opaque) {
|
|
41
|
+
render_arguments *arguments = (render_arguments *)opaque;
|
|
42
|
+
|
|
43
|
+
#ifdef __APPLE__
|
|
44
|
+
dispatch_apply(arguments->height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t y) {
|
|
45
|
+
render_scanline(arguments->pixels, arguments->width, arguments->height, (int)y,
|
|
46
|
+
arguments->resolution, arguments->uniforms);
|
|
47
|
+
});
|
|
48
|
+
#else
|
|
49
|
+
for (int y = 0; y < arguments->height; y++) {
|
|
50
|
+
render_scanline(arguments->pixels, arguments->width, arguments->height, y,
|
|
51
|
+
arguments->resolution, arguments->uniforms);
|
|
52
|
+
}
|
|
53
|
+
#endif
|
|
54
|
+
|
|
55
|
+
return NULL;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static VALUE shader_#{@context.name}_render(VALUE self, VALUE rb_buffer, VALUE rb_width, VALUE rb_height#{@context.uniform_argument_list}) {
|
|
59
|
+
long long requested_width = NUM2LL(rb_width);
|
|
60
|
+
long long requested_height = NUM2LL(rb_height);
|
|
61
|
+
if (requested_width <= 0 || requested_width > INT_MAX ||
|
|
62
|
+
requested_height <= 0 || requested_height > INT_MAX) {
|
|
63
|
+
rb_raise(rb_eArgError, "width and height must be positive integers no greater than INT_MAX");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
size_t width_size = (size_t)requested_width;
|
|
67
|
+
size_t height_size = (size_t)requested_height;
|
|
68
|
+
if (height_size > SIZE_MAX / width_size || width_size * height_size > SIZE_MAX / 4) {
|
|
69
|
+
rb_raise(rb_eRangeError, "render dimensions overflow the output buffer size");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
size_t required_bytes = width_size * height_size * 4;
|
|
73
|
+
int width = (int)requested_width;
|
|
74
|
+
int height = (int)requested_height;
|
|
75
|
+
vec2 resolution = vec2_new((float)width, (float)height);
|
|
76
|
+
|
|
77
|
+
Uniforms uniforms;
|
|
78
|
+
#{uniform_assignments}
|
|
79
|
+
|
|
80
|
+
Check_Type(rb_buffer, T_STRING);
|
|
81
|
+
rb_str_modify(rb_buffer);
|
|
82
|
+
if ((size_t)RSTRING_LEN(rb_buffer) < required_bytes) {
|
|
83
|
+
rb_raise(rb_eArgError, "pixel buffer is too small: need %zu bytes, got %ld",
|
|
84
|
+
required_bytes, RSTRING_LEN(rb_buffer));
|
|
85
|
+
}
|
|
86
|
+
uint8_t *pixels = (uint8_t *)RSTRING_PTR(rb_buffer);
|
|
87
|
+
|
|
88
|
+
render_arguments arguments = {pixels, width, height, resolution, uniforms};
|
|
89
|
+
rb_thread_call_without_gvl(render_without_gvl, &arguments, RUBY_UBF_IO, NULL);
|
|
90
|
+
|
|
91
|
+
return Qnil;
|
|
92
|
+
}
|
|
93
|
+
C
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def uniform_assignments
|
|
99
|
+
@context.uniform_entries.map do |uniform_name, spec|
|
|
100
|
+
generate_uniform_assignment(uniform_name, spec)
|
|
101
|
+
end.join("\n")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def generate_uniform_assignment(uniform_name, spec)
|
|
105
|
+
case spec.wrapper_kind
|
|
106
|
+
when :float
|
|
107
|
+
" uniforms.#{uniform_name} = (float)NUM2DBL(rb_#{uniform_name});"
|
|
108
|
+
when :int
|
|
109
|
+
" uniforms.#{uniform_name} = NUM2INT(rb_#{uniform_name});"
|
|
110
|
+
when :bool
|
|
111
|
+
" uniforms.#{uniform_name} = RTEST(rb_#{uniform_name}) ? 1 : 0;"
|
|
112
|
+
when :vector
|
|
113
|
+
generate_vector_uniform_assignment(uniform_name, spec.vector_size)
|
|
114
|
+
else
|
|
115
|
+
raise ArgumentError, "Unsupported compiled uniform type: #{spec.c_type}"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def generate_vector_uniform_assignment(uniform_name, vector_size)
|
|
120
|
+
constructor = VECTOR_CONSTRUCTORS.fetch(vector_size)
|
|
121
|
+
components = (0...vector_size).map do |index|
|
|
122
|
+
" (float)NUM2DBL(rb_ary_entry(rb_#{uniform_name}, #{index}))"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
<<~C.strip
|
|
126
|
+
Check_Type(rb_#{uniform_name}, T_ARRAY);
|
|
127
|
+
if (RARRAY_LEN(rb_#{uniform_name}) != #{vector_size}) {
|
|
128
|
+
rb_raise(rb_eArgError, "uniform #{uniform_name} must contain exactly #{vector_size} components");
|
|
129
|
+
}
|
|
130
|
+
uniforms.#{uniform_name} = #{constructor}(
|
|
131
|
+
#{components.join(",\n")}
|
|
132
|
+
);
|
|
133
|
+
C
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
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,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
class CodeGenerator
|
|
5
|
+
class TemplateContext
|
|
6
|
+
attr_reader :name, :extension_name
|
|
7
|
+
|
|
8
|
+
def initialize(name:, uniforms:, helpers_block:, fragment_block:, extension_name: name)
|
|
9
|
+
@name = RLSL.validate_shader_name!(name)
|
|
10
|
+
@extension_name = RLSL.validate_identifier!(extension_name, context: "extension name")
|
|
11
|
+
@uniforms = uniforms
|
|
12
|
+
@helpers_block = helpers_block
|
|
13
|
+
@fragment_block = fragment_block
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render_arity
|
|
17
|
+
3 + @uniforms.size
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def helpers_code
|
|
21
|
+
return "" unless @helpers_block
|
|
22
|
+
|
|
23
|
+
@helpers_block.call
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def fragment_code
|
|
27
|
+
return "" unless @fragment_block
|
|
28
|
+
|
|
29
|
+
@fragment_block.call
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def uniform_entries
|
|
33
|
+
@uniform_entries ||= @uniforms.map do |uniform_name, type|
|
|
34
|
+
[uniform_name, UniformTypes.compiled_spec(type)]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def uniform_argument_list
|
|
39
|
+
arguments = @uniforms.map { |uniform_name, _| "VALUE rb_#{uniform_name}" }
|
|
40
|
+
arguments.empty? ? "" : ", #{arguments.join(', ')}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|