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 +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +21 -0
- data/README.md +170 -0
- data/Rakefile +12 -0
- data/lib/rlsl/base_translator.rb +109 -0
- data/lib/rlsl/code_generator.rb +231 -0
- data/lib/rlsl/compiled_shader.rb +24 -0
- data/lib/rlsl/function_context.rb +35 -0
- data/lib/rlsl/glsl/translator.rb +97 -0
- data/lib/rlsl/msl/shader.rb +83 -0
- data/lib/rlsl/msl/translator.rb +89 -0
- data/lib/rlsl/prism/ast_visitor.rb +371 -0
- data/lib/rlsl/prism/builtins.rb +197 -0
- data/lib/rlsl/prism/emitters/base_emitter.rb +480 -0
- data/lib/rlsl/prism/emitters/c_emitter.rb +153 -0
- data/lib/rlsl/prism/emitters/glsl_emitter.rb +74 -0
- data/lib/rlsl/prism/emitters/msl_emitter.rb +76 -0
- data/lib/rlsl/prism/emitters/wgsl_emitter.rb +89 -0
- data/lib/rlsl/prism/ir/nodes.rb +373 -0
- data/lib/rlsl/prism/source_extractor.rb +152 -0
- data/lib/rlsl/prism/transpiler.rb +128 -0
- data/lib/rlsl/prism/type_inference.rb +289 -0
- data/lib/rlsl/shader_builder.rb +165 -0
- data/lib/rlsl/types.rb +36 -0
- data/lib/rlsl/uniform_context.rb +28 -0
- data/lib/rlsl/version.rb +5 -0
- data/lib/rlsl/wgsl/translator.rb +93 -0
- data/lib/rlsl.rb +57 -0
- metadata +100 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
class TypeInference
|
|
6
|
+
attr_reader :symbol_table
|
|
7
|
+
|
|
8
|
+
def initialize(uniforms = {}, custom_functions = {})
|
|
9
|
+
@symbol_table = {}
|
|
10
|
+
@uniforms = uniforms
|
|
11
|
+
@custom_functions = custom_functions
|
|
12
|
+
|
|
13
|
+
uniforms.each do |name, type|
|
|
14
|
+
@symbol_table[name.to_sym] = type
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def register(name, type)
|
|
19
|
+
@symbol_table[name.to_sym] = type
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def register_function(name, returns:)
|
|
23
|
+
@custom_functions[name.to_sym] = { returns: returns }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def lookup(name)
|
|
27
|
+
@symbol_table[name.to_sym]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def infer(node)
|
|
31
|
+
case node
|
|
32
|
+
when IR::Block
|
|
33
|
+
infer_block(node)
|
|
34
|
+
when IR::VarDecl
|
|
35
|
+
infer_var_decl(node)
|
|
36
|
+
when IR::VarRef
|
|
37
|
+
infer_var_ref(node)
|
|
38
|
+
when IR::Literal
|
|
39
|
+
infer_literal(node)
|
|
40
|
+
when IR::BoolLiteral
|
|
41
|
+
node.type = :bool
|
|
42
|
+
node
|
|
43
|
+
when IR::BinaryOp
|
|
44
|
+
infer_binary_op(node)
|
|
45
|
+
when IR::UnaryOp
|
|
46
|
+
infer_unary_op(node)
|
|
47
|
+
when IR::FuncCall
|
|
48
|
+
infer_func_call(node)
|
|
49
|
+
when IR::FieldAccess
|
|
50
|
+
infer_field_access(node)
|
|
51
|
+
when IR::Swizzle
|
|
52
|
+
infer_swizzle(node)
|
|
53
|
+
when IR::IfStatement
|
|
54
|
+
infer_if_statement(node)
|
|
55
|
+
when IR::Return
|
|
56
|
+
infer_return(node)
|
|
57
|
+
when IR::Assignment
|
|
58
|
+
infer_assignment(node)
|
|
59
|
+
when IR::ForLoop
|
|
60
|
+
infer_for_loop(node)
|
|
61
|
+
when IR::WhileLoop
|
|
62
|
+
infer_while_loop(node)
|
|
63
|
+
when IR::Parenthesized
|
|
64
|
+
infer_parenthesized(node)
|
|
65
|
+
when IR::FunctionDefinition
|
|
66
|
+
infer_function_definition(node)
|
|
67
|
+
when IR::ArrayLiteral
|
|
68
|
+
infer_array_literal(node)
|
|
69
|
+
when IR::ArrayIndex
|
|
70
|
+
infer_array_index(node)
|
|
71
|
+
when IR::GlobalDecl
|
|
72
|
+
infer_global_decl(node)
|
|
73
|
+
when IR::MultipleAssignment
|
|
74
|
+
infer_multiple_assignment(node)
|
|
75
|
+
else
|
|
76
|
+
node
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def infer_block(node)
|
|
83
|
+
node.statements.each { |stmt| infer(stmt) }
|
|
84
|
+
node.type = node.statements.last&.type
|
|
85
|
+
node
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def infer_var_decl(node)
|
|
89
|
+
infer(node.initializer) if node.initializer
|
|
90
|
+
node.type ||= node.initializer&.type
|
|
91
|
+
register(node.name, node.type) if node.type
|
|
92
|
+
node
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def infer_var_ref(node)
|
|
96
|
+
node.type ||= lookup(node.name)
|
|
97
|
+
node
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def infer_literal(node)
|
|
101
|
+
node
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def infer_binary_op(node)
|
|
105
|
+
infer(node.left)
|
|
106
|
+
infer(node.right)
|
|
107
|
+
|
|
108
|
+
node.type = Builtins.binary_op_result_type(
|
|
109
|
+
node.operator,
|
|
110
|
+
node.left.type,
|
|
111
|
+
node.right.type
|
|
112
|
+
)
|
|
113
|
+
node
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def infer_unary_op(node)
|
|
117
|
+
infer(node.operand)
|
|
118
|
+
|
|
119
|
+
case node.operator.to_s
|
|
120
|
+
when "-"
|
|
121
|
+
node.type = node.operand.type
|
|
122
|
+
when "!"
|
|
123
|
+
node.type = :bool
|
|
124
|
+
end
|
|
125
|
+
node
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def infer_func_call(node)
|
|
129
|
+
node.args.each { |arg| infer(arg) }
|
|
130
|
+
infer(node.receiver) if node.receiver
|
|
131
|
+
|
|
132
|
+
sig = Builtins.function_signature(node.name)
|
|
133
|
+
if sig
|
|
134
|
+
arg_types = node.args.map(&:type)
|
|
135
|
+
node.type = Builtins.resolve_return_type(sig[:returns], arg_types)
|
|
136
|
+
elsif @custom_functions.key?(node.name.to_sym)
|
|
137
|
+
node.type = @custom_functions[node.name.to_sym][:returns]
|
|
138
|
+
else
|
|
139
|
+
node.type = node.receiver&.type
|
|
140
|
+
end
|
|
141
|
+
node
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def infer_field_access(node)
|
|
145
|
+
infer(node.receiver)
|
|
146
|
+
|
|
147
|
+
if Builtins.single_component_field?(node.field)
|
|
148
|
+
node.type = :float
|
|
149
|
+
else
|
|
150
|
+
node.type = @uniforms[node.field.to_sym] || :float
|
|
151
|
+
end
|
|
152
|
+
node
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def infer_swizzle(node)
|
|
156
|
+
infer(node.receiver)
|
|
157
|
+
node.type = Builtins.swizzle_type(node.components)
|
|
158
|
+
node
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def infer_if_statement(node)
|
|
162
|
+
infer(node.condition)
|
|
163
|
+
infer(node.then_branch)
|
|
164
|
+
infer(node.else_branch) if node.else_branch
|
|
165
|
+
|
|
166
|
+
node.type = node.then_branch.type
|
|
167
|
+
node
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def infer_return(node)
|
|
171
|
+
infer(node.expression) if node.expression
|
|
172
|
+
node.type = node.expression&.type
|
|
173
|
+
node
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def infer_assignment(node)
|
|
177
|
+
infer(node.target)
|
|
178
|
+
infer(node.value)
|
|
179
|
+
node.type = node.value.type
|
|
180
|
+
node
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def infer_for_loop(node)
|
|
184
|
+
register(node.variable, :int)
|
|
185
|
+
infer(node.range_start)
|
|
186
|
+
infer(node.range_end)
|
|
187
|
+
infer(node.body)
|
|
188
|
+
node.type = nil
|
|
189
|
+
node
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def infer_while_loop(node)
|
|
193
|
+
infer(node.condition)
|
|
194
|
+
infer(node.body)
|
|
195
|
+
node.type = nil
|
|
196
|
+
node
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def infer_function_definition(node)
|
|
200
|
+
node.param_types.each do |param_name, param_type|
|
|
201
|
+
register(param_name, param_type)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
infer(node.body)
|
|
205
|
+
|
|
206
|
+
node.return_type ||= node.body&.type
|
|
207
|
+
node.type = node.return_type
|
|
208
|
+
node
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def infer_parenthesized(node)
|
|
212
|
+
infer(node.expression)
|
|
213
|
+
node.type = node.expression.type
|
|
214
|
+
node
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def infer_array_literal(node)
|
|
218
|
+
node.elements.each { |elem| infer(elem) }
|
|
219
|
+
|
|
220
|
+
element_type = node.elements.first&.type || :float
|
|
221
|
+
node.type = :"array_#{element_type}"
|
|
222
|
+
node
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def infer_array_index(node)
|
|
226
|
+
infer(node.array)
|
|
227
|
+
infer(node.index)
|
|
228
|
+
|
|
229
|
+
array_type = node.array.type
|
|
230
|
+
if array_type.to_s.start_with?("array_")
|
|
231
|
+
node.type = array_type.to_s.sub("array_", "").to_sym
|
|
232
|
+
else
|
|
233
|
+
node.type = @symbol_table["#{node.array.name}_element_type".to_sym] || :float
|
|
234
|
+
end
|
|
235
|
+
node
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def infer_global_decl(node)
|
|
239
|
+
infer(node.initializer) if node.initializer
|
|
240
|
+
|
|
241
|
+
if node.initializer.is_a?(IR::ArrayLiteral)
|
|
242
|
+
node.array_size ||= node.initializer.elements.length
|
|
243
|
+
first_elem = node.initializer.elements.first
|
|
244
|
+
node.element_type ||= first_elem&.type || :float
|
|
245
|
+
node.type = :"array_#{node.element_type}"
|
|
246
|
+
else
|
|
247
|
+
node.type ||= node.initializer&.type
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
register(node.name, node.type) if node.type
|
|
251
|
+
|
|
252
|
+
if node.element_type
|
|
253
|
+
register("#{node.name}_element_type".to_sym, node.element_type)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
node
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def infer_multiple_assignment(node)
|
|
260
|
+
infer(node.value)
|
|
261
|
+
|
|
262
|
+
value_type = node.value.type
|
|
263
|
+
if value_type.is_a?(IR::TupleType)
|
|
264
|
+
node.targets.each_with_index do |target, i|
|
|
265
|
+
target.type = value_type.types[i]
|
|
266
|
+
register(target.name, target.type)
|
|
267
|
+
end
|
|
268
|
+
elsif value_type.to_s.start_with?("array_")
|
|
269
|
+
elem_type = value_type.to_s.sub("array_", "").to_sym
|
|
270
|
+
node.targets.each do |target|
|
|
271
|
+
target.type = elem_type
|
|
272
|
+
register(target.name, target.type)
|
|
273
|
+
end
|
|
274
|
+
elsif @custom_functions.key?(node.value.name) && node.value.is_a?(IR::FuncCall)
|
|
275
|
+
func_info = @custom_functions[node.value.name]
|
|
276
|
+
if func_info[:returns].is_a?(Array)
|
|
277
|
+
node.targets.each_with_index do |target, i|
|
|
278
|
+
target.type = func_info[:returns][i]
|
|
279
|
+
register(target.name, target.type)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
node.type = nil
|
|
285
|
+
node
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "digest"
|
|
5
|
+
require "rbconfig"
|
|
6
|
+
|
|
7
|
+
module RLSL
|
|
8
|
+
class ShaderBuilder
|
|
9
|
+
attr_reader :name
|
|
10
|
+
|
|
11
|
+
def initialize(name)
|
|
12
|
+
@name = name.to_s
|
|
13
|
+
@uniforms = {}
|
|
14
|
+
@fragment_mode = :c
|
|
15
|
+
@helpers_mode = :c
|
|
16
|
+
@custom_functions = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def uniforms(&block)
|
|
20
|
+
if block_given?
|
|
21
|
+
ctx = UniformContext.new
|
|
22
|
+
ctx.instance_eval(&block)
|
|
23
|
+
@uniforms = ctx.uniforms
|
|
24
|
+
else
|
|
25
|
+
@uniforms
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def helpers(mode = :ruby, &block)
|
|
30
|
+
@helpers_block = block
|
|
31
|
+
@helpers_mode = mode
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def functions(&block)
|
|
35
|
+
ctx = FunctionContext.new
|
|
36
|
+
ctx.instance_eval(&block)
|
|
37
|
+
@custom_functions = ctx.functions
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def fragment(&block)
|
|
41
|
+
@fragment_block = block
|
|
42
|
+
@fragment_mode = block.arity > 0 ? :ruby : :c
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def ruby_mode?
|
|
46
|
+
@fragment_mode == :ruby
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def compile_and_load
|
|
50
|
+
c_code = generate_c_code
|
|
51
|
+
code_hash = Digest::MD5.hexdigest(c_code)[0..7]
|
|
52
|
+
ext_name = "#{@name}_#{code_hash}"
|
|
53
|
+
ext_dir = File.join(RLSL.cache_dir, ext_name)
|
|
54
|
+
ext_file = File.join(ext_dir, "#{@name}.#{RbConfig::CONFIG['DLEXT']}")
|
|
55
|
+
|
|
56
|
+
unless File.exist?(ext_file)
|
|
57
|
+
compile_extension(@name, ext_dir, c_code)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
require ext_file
|
|
61
|
+
CompiledShader.new(@name, ext_name, @uniforms.keys)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def build_metal_shader
|
|
65
|
+
if ruby_mode?
|
|
66
|
+
fragment_code = transpile_fragment(:msl)
|
|
67
|
+
helpers_code = @helpers_block ? @helpers_block.call : ""
|
|
68
|
+
else
|
|
69
|
+
helpers_code = @helpers_block ? @helpers_block.call : ""
|
|
70
|
+
fragment_code = @fragment_block ? @fragment_block.call : ""
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
translator = MSL::Translator.new(@uniforms, helpers_code, fragment_code)
|
|
74
|
+
msl_source = translator.translate
|
|
75
|
+
|
|
76
|
+
MSL::Shader.new(@name, @uniforms, msl_source)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def build_wgsl_shader
|
|
80
|
+
if ruby_mode?
|
|
81
|
+
fragment_code = transpile_fragment(:wgsl)
|
|
82
|
+
helpers_code = @helpers_block ? @helpers_block.call : ""
|
|
83
|
+
else
|
|
84
|
+
helpers_code = @helpers_block ? @helpers_block.call : ""
|
|
85
|
+
fragment_code = @fragment_block ? @fragment_block.call : ""
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
translator = WGSL::Translator.new(@uniforms, helpers_code, fragment_code)
|
|
89
|
+
translator.translate
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def build_glsl_shader(version: "450")
|
|
93
|
+
if ruby_mode?
|
|
94
|
+
fragment_code = transpile_fragment(:glsl)
|
|
95
|
+
helpers_code = @helpers_block ? @helpers_block.call : ""
|
|
96
|
+
else
|
|
97
|
+
helpers_code = @helpers_block ? @helpers_block.call : ""
|
|
98
|
+
fragment_code = @fragment_block ? @fragment_block.call : ""
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
translator = GLSL::Translator.new(@uniforms, helpers_code, fragment_code, version: version)
|
|
102
|
+
translator.translate
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def transpile_fragment(target)
|
|
106
|
+
return "" unless @fragment_block
|
|
107
|
+
|
|
108
|
+
transpiler = Prism::Transpiler.new(@uniforms, @custom_functions)
|
|
109
|
+
transpiler.transpile(@fragment_block, target)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def transpile_helpers(target)
|
|
113
|
+
return "" unless @helpers_block
|
|
114
|
+
|
|
115
|
+
transpiler = Prism::Transpiler.new(@uniforms, @custom_functions)
|
|
116
|
+
transpiler.transpile_helpers(@helpers_block, target, @custom_functions)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def helpers_ruby_mode?
|
|
120
|
+
@helpers_mode == :ruby
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
|
|
125
|
+
def generate_c_code
|
|
126
|
+
if helpers_ruby_mode?
|
|
127
|
+
helpers_code = transpile_helpers(:c)
|
|
128
|
+
helpers_block = -> { helpers_code }
|
|
129
|
+
else
|
|
130
|
+
helpers_block = @helpers_block
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if ruby_mode?
|
|
134
|
+
fragment_code = transpile_fragment(:c)
|
|
135
|
+
fragment_block = -> { fragment_code }
|
|
136
|
+
else
|
|
137
|
+
fragment_block = @fragment_block
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
codegen = CodeGenerator.new(@name, @uniforms, helpers_block, fragment_block)
|
|
141
|
+
codegen.generate
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def compile_extension(ext_name, ext_dir, c_code)
|
|
145
|
+
FileUtils.mkdir_p(ext_dir)
|
|
146
|
+
|
|
147
|
+
File.write(File.join(ext_dir, "#{ext_name}.c"), c_code)
|
|
148
|
+
|
|
149
|
+
extconf = <<~RUBY
|
|
150
|
+
require "mkmf"
|
|
151
|
+
$CFLAGS << " -O3 -ffast-math"
|
|
152
|
+
if RUBY_PLATFORM =~ /darwin/
|
|
153
|
+
$CFLAGS << " -fblocks"
|
|
154
|
+
end
|
|
155
|
+
create_makefile("#{ext_name}")
|
|
156
|
+
RUBY
|
|
157
|
+
File.write(File.join(ext_dir, "extconf.rb"), extconf)
|
|
158
|
+
|
|
159
|
+
Dir.chdir(ext_dir) do
|
|
160
|
+
system("#{RbConfig.ruby} extconf.rb > /dev/null 2>&1") or raise "extconf failed for #{ext_name}"
|
|
161
|
+
system("/usr/bin/make > /dev/null 2>&1") or raise "make failed for #{ext_name}"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
data/lib/rlsl/types.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
# C type definitions for the shader system
|
|
5
|
+
C_TYPES = <<~C
|
|
6
|
+
typedef struct { float x, y; } vec2;
|
|
7
|
+
typedef struct { float x, y, z; } vec3;
|
|
8
|
+
typedef struct { float x, y, z, w; } vec4;
|
|
9
|
+
typedef struct { float m[4]; } mat2;
|
|
10
|
+
typedef struct { float m[9]; } mat3;
|
|
11
|
+
typedef struct { float m[16]; } mat4;
|
|
12
|
+
typedef struct { void* data; int width; int height; } sampler2D;
|
|
13
|
+
|
|
14
|
+
#define PI 3.14159265f
|
|
15
|
+
#define TAU 6.28318530f
|
|
16
|
+
C
|
|
17
|
+
|
|
18
|
+
# Uniform type symbols
|
|
19
|
+
UNIFORM_TYPES = %i[float vec2 vec3 vec4 int bool mat2 mat3 mat4 sampler2D].freeze
|
|
20
|
+
|
|
21
|
+
# Type mapping helper
|
|
22
|
+
module TypeMapping
|
|
23
|
+
C_UNIFORM_TYPES = {
|
|
24
|
+
float: "float",
|
|
25
|
+
int: "int",
|
|
26
|
+
bool: "int",
|
|
27
|
+
vec2: "vec2",
|
|
28
|
+
vec3: "vec3",
|
|
29
|
+
vec4: "vec4",
|
|
30
|
+
mat2: "mat2",
|
|
31
|
+
mat3: "mat3",
|
|
32
|
+
mat4: "mat4",
|
|
33
|
+
sampler2D: "sampler2D"
|
|
34
|
+
}.freeze
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
# DSL context for defining uniform variables
|
|
5
|
+
class UniformContext
|
|
6
|
+
attr_reader :uniforms
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@uniforms = {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def float(name)
|
|
13
|
+
@uniforms[name] = :float
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def vec2(name)
|
|
17
|
+
@uniforms[name] = :vec2
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def vec3(name)
|
|
21
|
+
@uniforms[name] = :vec3
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def vec4(name)
|
|
25
|
+
@uniforms[name] = :vec4
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/rlsl/version.rb
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module WGSL
|
|
5
|
+
class Translator < BaseTranslator
|
|
6
|
+
TYPE_MAP = {
|
|
7
|
+
"vec2" => "vec2<f32>",
|
|
8
|
+
"vec3" => "vec3<f32>",
|
|
9
|
+
"vec4" => "vec4<f32>"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
FUNC_REPLACEMENTS = BaseTranslator.common_func_replacements(
|
|
13
|
+
target_vec2: "vec2<f32>",
|
|
14
|
+
target_vec3: "vec3<f32>",
|
|
15
|
+
target_vec4: "vec4<f32>"
|
|
16
|
+
).freeze
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
|
|
20
|
+
def translate_code(c_code)
|
|
21
|
+
result = super(c_code)
|
|
22
|
+
return result if result.empty?
|
|
23
|
+
|
|
24
|
+
result.gsub!(/\bstatic\s+/, "")
|
|
25
|
+
result.gsub!(/\binline\s+/, "")
|
|
26
|
+
result.gsub!(/\bfloat\b/, "f32")
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate_shader(helpers, fragment)
|
|
31
|
+
<<~WGSL
|
|
32
|
+
// WGSL Shader generated by RLSL
|
|
33
|
+
|
|
34
|
+
struct Uniforms {
|
|
35
|
+
#{generate_uniform_struct}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
39
|
+
@group(0) @binding(1) var output_texture: texture_storage_2d<rgba8unorm, write>;
|
|
40
|
+
|
|
41
|
+
#{helpers}
|
|
42
|
+
|
|
43
|
+
fn shader_fragment(frag_coord: vec2<f32>, resolution: vec2<f32>) -> vec3<f32> {
|
|
44
|
+
let uv = frag_coord / resolution.y;
|
|
45
|
+
#{fragment}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@compute @workgroup_size(8, 8)
|
|
49
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
50
|
+
let resolution = vec2<f32>(f32(textureDimensions(output_texture).x),
|
|
51
|
+
f32(textureDimensions(output_texture).y));
|
|
52
|
+
if (gid.x >= u32(resolution.x) || gid.y >= u32(resolution.y)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let frag_coord = vec2<f32>(f32(gid.x), resolution.y - 1.0 - f32(gid.y));
|
|
57
|
+
let color = shader_fragment(frag_coord, resolution);
|
|
58
|
+
|
|
59
|
+
textureStore(output_texture, vec2<i32>(i32(gid.x), i32(gid.y)),
|
|
60
|
+
vec4<f32>(clamp(color, vec3<f32>(0.0), vec3<f32>(1.0)), 1.0));
|
|
61
|
+
}
|
|
62
|
+
WGSL
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def generate_uniform_struct
|
|
68
|
+
fields = ["resolution: vec2<f32>,"]
|
|
69
|
+
@uniforms.each do |name, type|
|
|
70
|
+
wgsl_type = uniform_type_to_target(type)
|
|
71
|
+
fields << "#{name}: #{wgsl_type},"
|
|
72
|
+
end
|
|
73
|
+
fields.join("\n ")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def target_float_type
|
|
77
|
+
"f32"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def target_vec2_type
|
|
81
|
+
"vec2<f32>"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def target_vec3_type
|
|
85
|
+
"vec3<f32>"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def target_vec4_type
|
|
89
|
+
"vec4<f32>"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/lib/rlsl.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
require_relative "rlsl/version"
|
|
6
|
+
require_relative "rlsl/types"
|
|
7
|
+
require_relative "rlsl/uniform_context"
|
|
8
|
+
require_relative "rlsl/function_context"
|
|
9
|
+
require_relative "rlsl/code_generator"
|
|
10
|
+
require_relative "rlsl/compiled_shader"
|
|
11
|
+
require_relative "rlsl/base_translator"
|
|
12
|
+
require_relative "rlsl/msl/translator"
|
|
13
|
+
require_relative "rlsl/msl/shader"
|
|
14
|
+
require_relative "rlsl/wgsl/translator"
|
|
15
|
+
require_relative "rlsl/glsl/translator"
|
|
16
|
+
require_relative "rlsl/prism/transpiler"
|
|
17
|
+
require_relative "rlsl/shader_builder"
|
|
18
|
+
|
|
19
|
+
module RLSL
|
|
20
|
+
CACHE_DIR = File.expand_path("~/.cache/rlsl/compiled")
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def define(name, &block)
|
|
24
|
+
builder = ShaderBuilder.new(name)
|
|
25
|
+
builder.instance_eval(&block)
|
|
26
|
+
builder.compile_and_load
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def define_metal(name, &block)
|
|
30
|
+
builder = ShaderBuilder.new(name)
|
|
31
|
+
builder.instance_eval(&block)
|
|
32
|
+
builder.build_metal_shader
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_wgsl(name, &block)
|
|
36
|
+
builder = ShaderBuilder.new(name)
|
|
37
|
+
builder.instance_eval(&block)
|
|
38
|
+
builder.build_wgsl_shader
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_glsl(name, version: "450", &block)
|
|
42
|
+
builder = ShaderBuilder.new(name)
|
|
43
|
+
builder.instance_eval(&block)
|
|
44
|
+
builder.build_glsl_shader(version: version)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def cache_dir
|
|
48
|
+
@cache_dir ||= begin
|
|
49
|
+
FileUtils.mkdir_p(CACHE_DIR)
|
|
50
|
+
CACHE_DIR
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module CompiledShaders
|
|
56
|
+
end
|
|
57
|
+
end
|