rlsl 1.0.0 → 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 +3 -1
- data/README.md +101 -28
- data/Rakefile +7 -0
- data/lib/rlsl/base_translator/call_parser.rb +13 -2
- data/lib/rlsl/base_translator/code_scanner.rb +39 -1
- data/lib/rlsl/base_translator.rb +24 -2
- data/lib/rlsl/code_generator/math_prelude.rb +103 -10
- data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +52 -12
- data/lib/rlsl/code_generator/template_context.rb +6 -3
- data/lib/rlsl/code_generator/uniform_struct_generator.rb +1 -1
- data/lib/rlsl/code_generator.rb +7 -4
- data/lib/rlsl/compiled_shader.rb +1 -1
- data/lib/rlsl/errors.rb +29 -0
- data/lib/rlsl/glsl/translator.rb +19 -6
- data/lib/rlsl/msl/shader.rb +27 -9
- data/lib/rlsl/msl/translator.rb +25 -4
- data/lib/rlsl/msl/uniform_buffer_packer.rb +11 -7
- data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +59 -18
- data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +22 -9
- data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +18 -14
- data/lib/rlsl/prism/ast_visitor/scope_context.rb +4 -0
- data/lib/rlsl/prism/ast_visitor.rb +78 -20
- data/lib/rlsl/prism/builtins/function_registry.rb +41 -24
- data/lib/rlsl/prism/builtins/operator_rules.rb +24 -0
- data/lib/rlsl/prism/builtins/swizzle_rules.rb +10 -1
- data/lib/rlsl/prism/builtins.rb +8 -0
- data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +51 -8
- data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +42 -12
- data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +20 -4
- data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +7 -1
- data/lib/rlsl/prism/emitters/base_emitter.rb +66 -5
- data/lib/rlsl/prism/emitters/c_emitter.rb +103 -7
- data/lib/rlsl/prism/emitters/glsl_emitter.rb +48 -0
- data/lib/rlsl/prism/emitters/msl_emitter.rb +43 -1
- data/lib/rlsl/prism/emitters/target_emitter.rb +56 -9
- data/lib/rlsl/prism/emitters/wgsl_emitter.rb +173 -18
- data/lib/rlsl/prism/errors.rb +9 -0
- data/lib/rlsl/prism/ir/control_flow.rb +6 -4
- data/lib/rlsl/prism/ir/definitions.rb +17 -2
- data/lib/rlsl/prism/ir/expressions.rb +5 -1
- data/lib/rlsl/prism/ir/node.rb +1 -1
- data/lib/rlsl/prism/ir/traversal.rb +6 -2
- 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 +23 -25
- data/lib/rlsl/prism/source_extractor.rb +7 -6
- data/lib/rlsl/prism/source_unit/parser.rb +42 -32
- data/lib/rlsl/prism/source_unit.rb +10 -10
- data/lib/rlsl/prism/target_capability_validator.rb +27 -2
- data/lib/rlsl/prism/transpiler.rb +40 -3
- data/lib/rlsl/prism/type_inference/call_type_resolver.rb +28 -4
- data/lib/rlsl/prism/type_inference/call_validator.rb +5 -1
- data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +45 -19
- data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +88 -7
- data/lib/rlsl/prism/type_inference/definition_inferer.rb +16 -1
- data/lib/rlsl/prism/type_inference/expression_inferer.rb +4 -0
- data/lib/rlsl/prism/type_inference/field_type_resolver.rb +23 -2
- data/lib/rlsl/prism/type_inference/scope_stack.rb +1 -1
- data/lib/rlsl/prism/type_inference/type_shapes.rb +3 -3
- data/lib/rlsl/prism/type_inference.rb +10 -5
- data/lib/rlsl/shader_builder/build_service.rb +34 -7
- data/lib/rlsl/shader_builder/native_extension_compiler.rb +50 -24
- data/lib/rlsl/shader_builder/shader_definition.rb +3 -3
- data/lib/rlsl/shader_builder/source_resolver.rb +19 -1
- data/lib/rlsl/shader_builder.rb +50 -10
- data/lib/rlsl/shader_name.rb +18 -0
- data/lib/rlsl/types/type_spec.rb +3 -3
- data/lib/rlsl/types/value_normalizer.rb +16 -11
- data/lib/rlsl/types.rb +0 -4
- data/lib/rlsl/uniform_context.rb +18 -1
- data/lib/rlsl/version.rb +1 -1
- data/lib/rlsl/wgsl/translator.rb +29 -6
- data/lib/rlsl/wgsl/uniform_layout.rb +25 -0
- data/lib/rlsl.rb +24 -3
- metadata +23 -11
|
@@ -8,9 +8,30 @@ module RLSL
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def resolve(node)
|
|
11
|
-
|
|
11
|
+
if node.receiver.type == :uniforms
|
|
12
|
+
type = @uniforms[node.field.to_sym]
|
|
13
|
+
return type if type
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
raise SignatureError, "Unknown uniform field #{node.field.inspect}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if Builtins.single_component_field?(node.field)
|
|
19
|
+
unless Builtins.valid_swizzle_for_type?(node.field, node.receiver.type)
|
|
20
|
+
raise SignatureError, "Invalid component #{node.field.inspect} for #{node.receiver.type || :unknown}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return :float
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if Builtins.swizzle?(node.field)
|
|
27
|
+
unless Builtins.valid_swizzle_for_type?(node.field, node.receiver.type)
|
|
28
|
+
raise SignatureError, "Invalid swizzle #{node.field.inspect} for #{node.receiver.type || :unknown}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
return Builtins.swizzle_type(node.field)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
raise SignatureError, "Unknown field #{node.field.inspect} for #{node.receiver.type || :unknown}"
|
|
14
35
|
end
|
|
15
36
|
end
|
|
16
37
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module RLSL
|
|
4
4
|
module Prism
|
|
5
5
|
module TypeShapes
|
|
6
|
-
ArrayType = Struct.new(:element_type) do
|
|
6
|
+
ArrayType = Struct.new(:element_type, :element_count) do
|
|
7
7
|
def to_sym
|
|
8
8
|
:"array_#{element_type}"
|
|
9
9
|
end
|
|
@@ -15,8 +15,8 @@ module RLSL
|
|
|
15
15
|
|
|
16
16
|
module_function
|
|
17
17
|
|
|
18
|
-
def array(element_type)
|
|
19
|
-
ArrayType.new(element_type)
|
|
18
|
+
def array(element_type, length = nil)
|
|
19
|
+
ArrayType.new(element_type, length)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def array?(type)
|
|
@@ -14,7 +14,7 @@ require_relative "type_inference/control_flow_inferer"
|
|
|
14
14
|
|
|
15
15
|
module RLSL
|
|
16
16
|
module Prism
|
|
17
|
-
class SignatureError <
|
|
17
|
+
class SignatureError < RLSL::Error; end
|
|
18
18
|
|
|
19
19
|
class TypeInference
|
|
20
20
|
EXPRESSION_NODES = {
|
|
@@ -73,19 +73,21 @@ module RLSL
|
|
|
73
73
|
)
|
|
74
74
|
@definition_inferer = DefinitionInferer.new(
|
|
75
75
|
infer: method(:infer),
|
|
76
|
+
lookup: method(:lookup),
|
|
76
77
|
register: method(:register),
|
|
77
78
|
collection_type_resolver: @collection_type_resolver
|
|
78
79
|
)
|
|
79
80
|
@control_flow_inferer = ControlFlowInferer.new(
|
|
80
81
|
infer: method(:infer),
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
infer_in_scope: method(:infer_in_scope),
|
|
83
|
+
lookup: method(:lookup)
|
|
83
84
|
)
|
|
84
85
|
register_inferers
|
|
85
86
|
|
|
86
87
|
uniforms.each do |name, type|
|
|
87
88
|
register(name, type)
|
|
88
89
|
end
|
|
90
|
+
register(:u, :uniforms)
|
|
89
91
|
|
|
90
92
|
globals.each do |name, type|
|
|
91
93
|
register(name, type)
|
|
@@ -100,8 +102,8 @@ module RLSL
|
|
|
100
102
|
@types.register(name, type)
|
|
101
103
|
end
|
|
102
104
|
|
|
103
|
-
def register_function(name, returns:)
|
|
104
|
-
@custom_functions[name.to_sym] = { returns: returns }
|
|
105
|
+
def register_function(name, returns:, params: {})
|
|
106
|
+
@custom_functions[name.to_sym] = { returns: returns, params: params }
|
|
105
107
|
end
|
|
106
108
|
|
|
107
109
|
def lookup(name)
|
|
@@ -111,6 +113,9 @@ module RLSL
|
|
|
111
113
|
def infer(node, scoped: false)
|
|
112
114
|
options = node.is_a?(IR::Block) ? { scoped: scoped } : {}
|
|
113
115
|
@inferer_registry.infer(node, **options) || node
|
|
116
|
+
rescue RLSL::Error => error
|
|
117
|
+
error.with_source_location(node.location)
|
|
118
|
+
raise
|
|
114
119
|
end
|
|
115
120
|
|
|
116
121
|
private
|
|
@@ -11,26 +11,35 @@ module RLSL
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def compile_and_load
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
compiler = native_extension_compiler
|
|
15
|
+
base_code = generate_c_code
|
|
16
|
+
extension_name = compiler.extension_name_for(base_code)
|
|
17
|
+
c_code = generate_c_code(extension_name: extension_name)
|
|
18
|
+
artifact = compiler.build(c_code, ext_name: extension_name)
|
|
16
19
|
|
|
17
20
|
require artifact.file
|
|
18
21
|
CompiledShader.new(@name, artifact.ext_name, @definition.uniforms)
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def build_metal_shader
|
|
22
|
-
translator = MSL::Translator.new(@definition.uniforms, *translation_sources(:msl))
|
|
25
|
+
translator = MSL::Translator.new(@definition.uniforms, *translation_sources(:msl), name: @name)
|
|
23
26
|
msl_source = translator.translate
|
|
24
27
|
|
|
25
28
|
MSL::Shader.new(@name, @definition.uniforms, msl_source)
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def build_wgsl_shader
|
|
29
|
-
|
|
32
|
+
validate_wgsl_module_names!
|
|
33
|
+
WGSL::Translator.new(@definition.uniforms, *translation_sources(:wgsl), name: @name).translate
|
|
30
34
|
end
|
|
31
35
|
|
|
32
36
|
def build_glsl_shader(version: "450")
|
|
33
|
-
GLSL::Translator.new(
|
|
37
|
+
GLSL::Translator.new(
|
|
38
|
+
@definition.uniforms,
|
|
39
|
+
*translation_sources(:glsl),
|
|
40
|
+
version: version,
|
|
41
|
+
name: @name
|
|
42
|
+
).translate
|
|
34
43
|
end
|
|
35
44
|
|
|
36
45
|
def transpile_fragment(target)
|
|
@@ -47,9 +56,15 @@ module RLSL
|
|
|
47
56
|
|
|
48
57
|
private
|
|
49
58
|
|
|
50
|
-
def generate_c_code
|
|
59
|
+
def generate_c_code(extension_name: @name)
|
|
51
60
|
helpers_code, fragment_code = resolved_sources(:c)
|
|
52
|
-
codegen = CodeGenerator.new(
|
|
61
|
+
codegen = CodeGenerator.new(
|
|
62
|
+
@name,
|
|
63
|
+
@definition.uniforms,
|
|
64
|
+
-> { helpers_code },
|
|
65
|
+
-> { fragment_code },
|
|
66
|
+
extension_name: extension_name
|
|
67
|
+
)
|
|
53
68
|
codegen.generate
|
|
54
69
|
end
|
|
55
70
|
|
|
@@ -72,6 +87,18 @@ module RLSL
|
|
|
72
87
|
NativeExtensionCompiler.new(@name)
|
|
73
88
|
end
|
|
74
89
|
end
|
|
90
|
+
|
|
91
|
+
def validate_wgsl_module_names!
|
|
92
|
+
resources = @definition.uniforms.filter_map do |name, type|
|
|
93
|
+
[name, :"#{name}_sampler"] if type == :sampler2D
|
|
94
|
+
end.flatten
|
|
95
|
+
generated = %i[u output_texture rlsl_mod shader_fragment main]
|
|
96
|
+
functions = @definition.custom_functions.keys
|
|
97
|
+
conflicts = (resources & (generated + functions)) | (functions & generated)
|
|
98
|
+
return if conflicts.empty?
|
|
99
|
+
|
|
100
|
+
raise ArgumentError, "WGSL module name conflict: #{conflicts.join(', ')}"
|
|
101
|
+
end
|
|
75
102
|
end
|
|
76
103
|
end
|
|
77
104
|
end
|
|
@@ -1,55 +1,75 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "digest"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "shellwords"
|
|
4
6
|
|
|
5
7
|
module RLSL
|
|
6
8
|
class ShaderBuilder
|
|
7
9
|
class NativeExtensionCompiler
|
|
8
10
|
Artifact = Struct.new(:ext_name, :directory, :file, keyword_init: true)
|
|
9
11
|
|
|
10
|
-
def initialize(
|
|
11
|
-
|
|
12
|
+
def initialize(
|
|
13
|
+
shader_name,
|
|
14
|
+
cache_dir: RLSL.cache_dir,
|
|
15
|
+
ruby_bin: RbConfig.ruby,
|
|
16
|
+
dylib_ext: RbConfig::CONFIG["DLEXT"],
|
|
17
|
+
make_command: RbConfig::CONFIG["MAKE"] || "make",
|
|
18
|
+
fast_math: false
|
|
19
|
+
)
|
|
20
|
+
@shader_name = RLSL.validate_shader_name!(shader_name)
|
|
12
21
|
@cache_dir = cache_dir
|
|
13
22
|
@ruby_bin = ruby_bin
|
|
14
23
|
@dylib_ext = dylib_ext
|
|
24
|
+
@make_command = Shellwords.split(make_command)
|
|
25
|
+
@fast_math = fast_math
|
|
15
26
|
end
|
|
16
27
|
|
|
17
|
-
def build(c_code)
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
def build(c_code, ext_name: nil)
|
|
29
|
+
resolved_ext_name = ext_name || declared_extension_name(c_code) || extension_name_for(c_code)
|
|
30
|
+
artifact = artifact_for(c_code, ext_name: resolved_ext_name)
|
|
31
|
+
FileUtils.mkdir_p(artifact.directory)
|
|
32
|
+
File.open(File.join(artifact.directory, ".build.lock"), "w") do |lock|
|
|
33
|
+
lock.flock(File::LOCK_EX)
|
|
34
|
+
compile(artifact, c_code) unless File.exist?(artifact.file)
|
|
35
|
+
end
|
|
20
36
|
artifact
|
|
21
37
|
end
|
|
22
38
|
|
|
39
|
+
def extension_name_for(c_code)
|
|
40
|
+
code_hash = Digest::SHA256.hexdigest(c_code)[0, 16]
|
|
41
|
+
"#{@shader_name}_#{code_hash}"
|
|
42
|
+
end
|
|
43
|
+
|
|
23
44
|
private
|
|
24
45
|
|
|
25
|
-
def artifact_for(c_code)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
directory = File.join(@cache_dir, ext_name)
|
|
46
|
+
def artifact_for(c_code, ext_name: extension_name_for(c_code))
|
|
47
|
+
validated_ext_name = RLSL.validate_identifier!(ext_name, context: "extension name")
|
|
48
|
+
directory = File.join(@cache_dir, extension_name_for(c_code))
|
|
29
49
|
|
|
30
50
|
Artifact.new(
|
|
31
|
-
ext_name:
|
|
51
|
+
ext_name: validated_ext_name,
|
|
32
52
|
directory: directory,
|
|
33
|
-
file: File.join(directory, "#{
|
|
53
|
+
file: File.join(directory, "#{validated_ext_name}.#{@dylib_ext}")
|
|
34
54
|
)
|
|
35
55
|
end
|
|
36
56
|
|
|
37
57
|
def compile(artifact, c_code)
|
|
38
|
-
|
|
58
|
+
File.write(File.join(artifact.directory, "#{artifact.ext_name}.c"), c_code)
|
|
59
|
+
File.write(File.join(artifact.directory, "extconf.rb"), extconf_source(artifact.ext_name))
|
|
39
60
|
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
run_command!(@ruby_bin, "extconf.rb", chdir: artifact.directory)
|
|
62
|
+
run_command!(*@make_command, chdir: artifact.directory)
|
|
63
|
+
end
|
|
42
64
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
run_command("/usr/bin/make") or raise "make failed for #{artifact.ext_name}"
|
|
46
|
-
end
|
|
65
|
+
def declared_extension_name(c_code)
|
|
66
|
+
c_code[/\bvoid\s+Init_([A-Za-z_][A-Za-z0-9_]*)\s*\(/, 1]
|
|
47
67
|
end
|
|
48
68
|
|
|
49
69
|
def extconf_source(ext_name)
|
|
50
70
|
<<~RUBY
|
|
51
71
|
require "mkmf"
|
|
52
|
-
$CFLAGS << " -O3 -ffast-math"
|
|
72
|
+
$CFLAGS << " -O3#{@fast_math ? ' -ffast-math' : ''}"
|
|
53
73
|
if RUBY_PLATFORM =~ /darwin/
|
|
54
74
|
$CFLAGS << " -fblocks"
|
|
55
75
|
end
|
|
@@ -57,13 +77,19 @@ module RLSL
|
|
|
57
77
|
RUBY
|
|
58
78
|
end
|
|
59
79
|
|
|
60
|
-
def run_command(*args)
|
|
80
|
+
def run_command!(*args, chdir:)
|
|
81
|
+
runner = lambda do
|
|
82
|
+
stdout, stderr, status = Open3.capture3(*args, chdir: chdir)
|
|
83
|
+
return if status.success?
|
|
84
|
+
|
|
85
|
+
output = [stdout, stderr].reject(&:empty?).join("\n")
|
|
86
|
+
raise RLSL::CompilationError, "#{args.first} failed for #{@shader_name}:\n#{output}"
|
|
87
|
+
end
|
|
88
|
+
|
|
61
89
|
if defined?(Bundler) && Bundler.respond_to?(:with_unbundled_env)
|
|
62
|
-
Bundler.with_unbundled_env
|
|
63
|
-
system(*args, out: File::NULL, err: File::NULL)
|
|
64
|
-
end
|
|
90
|
+
Bundler.with_unbundled_env { runner.call }
|
|
65
91
|
else
|
|
66
|
-
|
|
92
|
+
runner.call
|
|
67
93
|
end
|
|
68
94
|
end
|
|
69
95
|
end
|
|
@@ -9,7 +9,7 @@ module RLSL
|
|
|
9
9
|
uniforms: {},
|
|
10
10
|
custom_functions: {},
|
|
11
11
|
helpers_block: nil,
|
|
12
|
-
helpers_mode: :
|
|
12
|
+
helpers_mode: :ruby,
|
|
13
13
|
fragment_block: nil,
|
|
14
14
|
fragment_mode: :c
|
|
15
15
|
)
|
|
@@ -38,11 +38,11 @@ module RLSL
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def ruby_fragment?
|
|
41
|
-
@fragment_mode
|
|
41
|
+
%i[ruby ruby_source].include?(@fragment_mode)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def ruby_helpers?
|
|
45
|
-
@helpers_mode
|
|
45
|
+
%i[ruby ruby_source].include?(@helpers_mode)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
private
|
|
@@ -28,6 +28,13 @@ module RLSL
|
|
|
28
28
|
|
|
29
29
|
def helpers_source(target)
|
|
30
30
|
return source_snippet("") unless @definition.helpers_block
|
|
31
|
+
if @definition.helpers_mode == :ruby_source
|
|
32
|
+
source = @definition.helpers_block.call
|
|
33
|
+
return source_snippet(
|
|
34
|
+
helpers_transpiler.transpile_helpers_source(source, target, @definition.custom_functions),
|
|
35
|
+
format: :target
|
|
36
|
+
)
|
|
37
|
+
end
|
|
31
38
|
return source_snippet(@definition.helpers_block.call) unless ruby_helpers?
|
|
32
39
|
|
|
33
40
|
source_snippet(
|
|
@@ -38,6 +45,10 @@ module RLSL
|
|
|
38
45
|
|
|
39
46
|
def fragment_source(target)
|
|
40
47
|
return source_snippet("") unless @definition.fragment_block
|
|
48
|
+
if @definition.fragment_mode == :ruby_source
|
|
49
|
+
source = @definition.fragment_block.call
|
|
50
|
+
return source_snippet(fragment_transpiler.transpile_source(source, target), format: :target)
|
|
51
|
+
end
|
|
41
52
|
return source_snippet(@definition.fragment_block.call) unless ruby_fragment?
|
|
42
53
|
|
|
43
54
|
source_snippet(fragment_transpiler.transpile(@definition.fragment_block, target), format: :target)
|
|
@@ -63,7 +74,14 @@ module RLSL
|
|
|
63
74
|
return {} unless ruby_helpers? && @definition.helpers_block
|
|
64
75
|
|
|
65
76
|
@helper_globals ||= begin
|
|
66
|
-
compilation =
|
|
77
|
+
compilation = if @definition.helpers_mode == :ruby_source
|
|
78
|
+
helpers_transpiler.compile_helpers_source(
|
|
79
|
+
@definition.helpers_block.call,
|
|
80
|
+
@definition.custom_functions
|
|
81
|
+
)
|
|
82
|
+
else
|
|
83
|
+
helpers_transpiler.compile_helpers(@definition.helpers_block, @definition.custom_functions)
|
|
84
|
+
end
|
|
67
85
|
extract_global_types(compilation.ir)
|
|
68
86
|
end
|
|
69
87
|
end
|
data/lib/rlsl/shader_builder.rb
CHANGED
|
@@ -10,7 +10,7 @@ module RLSL
|
|
|
10
10
|
attr_reader :name
|
|
11
11
|
|
|
12
12
|
def initialize(name, definition = ShaderDefinition.new)
|
|
13
|
-
@name = name
|
|
13
|
+
@name = RLSL.validate_shader_name!(name)
|
|
14
14
|
@definition = definition
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -18,27 +18,62 @@ module RLSL
|
|
|
18
18
|
if block_given?
|
|
19
19
|
ctx = UniformContext.new
|
|
20
20
|
ctx.instance_eval(&block)
|
|
21
|
-
@definition
|
|
21
|
+
update_definition(@definition.with_uniforms(ctx.uniforms))
|
|
22
22
|
else
|
|
23
23
|
@definition.uniforms
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
def uniform_types
|
|
28
|
+
@definition.uniforms
|
|
29
|
+
end
|
|
30
|
+
|
|
27
31
|
def helpers(mode = :ruby, &block)
|
|
28
|
-
|
|
32
|
+
raise ArgumentError, "helpers requires a block" unless block
|
|
33
|
+
|
|
34
|
+
resolved_mode = mode.to_sym
|
|
35
|
+
raise ArgumentError, "helpers mode must be :c or :ruby" unless %i[c ruby].include?(resolved_mode)
|
|
36
|
+
|
|
37
|
+
if resolved_mode == :ruby
|
|
38
|
+
source_unit = Prism::SourceExtractor.new.extract_unit(block)
|
|
39
|
+
update_definition(@definition.with_helpers(mode: :ruby_source, block: -> { source_unit }))
|
|
40
|
+
else
|
|
41
|
+
update_definition(@definition.with_helpers(mode: :c, block: block))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def helpers_source(source)
|
|
46
|
+
update_definition(@definition.with_helpers(mode: :ruby_source, block: -> { source.to_s }))
|
|
29
47
|
end
|
|
30
48
|
|
|
31
49
|
def functions(&block)
|
|
32
50
|
ctx = FunctionContext.new
|
|
33
51
|
ctx.instance_eval(&block)
|
|
34
|
-
@definition
|
|
52
|
+
update_definition(@definition.with_custom_functions(ctx.functions))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def fragment(mode = :auto, &block)
|
|
56
|
+
raise ArgumentError, "fragment requires a block" unless block
|
|
57
|
+
|
|
58
|
+
resolved_mode = if mode == :auto
|
|
59
|
+
block.parameters.empty? ? :c : :ruby
|
|
60
|
+
else
|
|
61
|
+
mode.to_sym
|
|
62
|
+
end
|
|
63
|
+
unless %i[c ruby].include?(resolved_mode)
|
|
64
|
+
raise ArgumentError, "fragment mode must be :c or :ruby"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if resolved_mode == :ruby
|
|
68
|
+
source_unit = Prism::SourceExtractor.new.extract_unit(block)
|
|
69
|
+
update_definition(@definition.with_fragment(mode: :ruby_source, block: -> { source_unit }))
|
|
70
|
+
else
|
|
71
|
+
update_definition(@definition.with_fragment(mode: :c, block: block))
|
|
72
|
+
end
|
|
35
73
|
end
|
|
36
74
|
|
|
37
|
-
def
|
|
38
|
-
@definition
|
|
39
|
-
mode: block.arity > 0 ? :ruby : :c,
|
|
40
|
-
block: block
|
|
41
|
-
)
|
|
75
|
+
def fragment_source(source)
|
|
76
|
+
update_definition(@definition.with_fragment(mode: :ruby_source, block: -> { source.to_s }))
|
|
42
77
|
end
|
|
43
78
|
|
|
44
79
|
def compile_and_load
|
|
@@ -68,7 +103,12 @@ module RLSL
|
|
|
68
103
|
private
|
|
69
104
|
|
|
70
105
|
def build_service
|
|
71
|
-
BuildService.new(@name, @definition)
|
|
106
|
+
@build_service ||= BuildService.new(@name, @definition)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def update_definition(definition)
|
|
110
|
+
@definition = definition
|
|
111
|
+
@build_service = nil
|
|
72
112
|
end
|
|
73
113
|
end
|
|
74
114
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
IDENTIFIER_PATTERN = /\A[A-Za-z_][A-Za-z0-9_]*\z/
|
|
5
|
+
SHADER_NAME_PATTERN = IDENTIFIER_PATTERN
|
|
6
|
+
|
|
7
|
+
def self.validate_identifier!(name, context: "identifier")
|
|
8
|
+
normalized = name.to_s
|
|
9
|
+
return normalized if normalized.match?(IDENTIFIER_PATTERN)
|
|
10
|
+
|
|
11
|
+
raise ArgumentError,
|
|
12
|
+
"Invalid #{context} #{name.inspect}: use an ASCII identifier beginning with a letter or underscore"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.validate_shader_name!(name)
|
|
16
|
+
validate_identifier!(name, context: "shader name")
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/rlsl/types/type_spec.rb
CHANGED
|
@@ -116,8 +116,8 @@ module RLSL
|
|
|
116
116
|
bool: UniformTypeSpec.new(
|
|
117
117
|
c_type: "int",
|
|
118
118
|
glsl_type: "bool",
|
|
119
|
-
wgsl_type: "
|
|
120
|
-
msl_type: "
|
|
119
|
+
wgsl_type: "i32",
|
|
120
|
+
msl_type: "int",
|
|
121
121
|
wrapper_kind: :bool,
|
|
122
122
|
metal_alignment: 4,
|
|
123
123
|
metal_size: 4,
|
|
@@ -156,7 +156,7 @@ module RLSL
|
|
|
156
156
|
c_type: "sampler2D",
|
|
157
157
|
glsl_type: "sampler2D",
|
|
158
158
|
wgsl_type: "texture_2d<f32>",
|
|
159
|
-
msl_type:
|
|
159
|
+
msl_type: "texture2d<float>",
|
|
160
160
|
compiled_supported: false,
|
|
161
161
|
runtime_supported: false,
|
|
162
162
|
function_shorthand: true
|
|
@@ -17,26 +17,24 @@ module RLSL
|
|
|
17
17
|
return value if type.nil?
|
|
18
18
|
|
|
19
19
|
spec = fetch(type)
|
|
20
|
-
raise
|
|
20
|
+
raise UniformValueError, unsupported_runtime_type_message(type, shader_name) unless spec.runtime_supported?
|
|
21
21
|
|
|
22
22
|
case spec.wrapper_kind
|
|
23
23
|
when :float
|
|
24
24
|
Float(value)
|
|
25
25
|
when :int
|
|
26
|
-
|
|
26
|
+
normalize_int(value, name: name, shader_name: shader_name)
|
|
27
27
|
when :bool
|
|
28
28
|
normalize_bool(value, name: name, shader_name: shader_name)
|
|
29
29
|
when :vector
|
|
30
30
|
normalize_vector(value, spec.vector_size, name: name, shader_name: shader_name)
|
|
31
31
|
else
|
|
32
|
-
raise
|
|
32
|
+
raise UniformValueError, unsupported_runtime_type_message(type, shader_name)
|
|
33
33
|
end
|
|
34
|
-
rescue
|
|
35
|
-
raise
|
|
36
|
-
rescue ArgumentError
|
|
37
|
-
raise
|
|
38
|
-
|
|
39
|
-
raise ArgumentError, invalid_uniform_message(name, type, value, shader_name)
|
|
34
|
+
rescue UniformValueError
|
|
35
|
+
raise
|
|
36
|
+
rescue TypeError, ArgumentError, RangeError
|
|
37
|
+
raise UniformValueError, invalid_uniform_message(name, type, value, shader_name)
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
def normalize_bool(value, name:, shader_name: nil)
|
|
@@ -44,12 +42,19 @@ module RLSL
|
|
|
44
42
|
return false if value == 0
|
|
45
43
|
return true if value == 1
|
|
46
44
|
|
|
47
|
-
raise
|
|
45
|
+
raise UniformValueError, invalid_uniform_message(name, :bool, value, shader_name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def normalize_int(value, name:, shader_name: nil)
|
|
49
|
+
integer = Integer(value)
|
|
50
|
+
return integer if (-2**31...2**31).cover?(integer)
|
|
51
|
+
|
|
52
|
+
raise UniformValueError, invalid_uniform_message(name, :int, value, shader_name)
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
def normalize_vector(value, vector_size, name:, shader_name: nil)
|
|
51
56
|
unless value.is_a?(Array) && value.length == vector_size
|
|
52
|
-
raise
|
|
57
|
+
raise UniformValueError, invalid_vector_message(name, vector_size, value, shader_name)
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
value.map { |component| Float(component) }
|
data/lib/rlsl/types.rb
CHANGED
data/lib/rlsl/uniform_context.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module RLSL
|
|
4
4
|
# DSL context for defining uniform variables
|
|
5
5
|
class UniformContext
|
|
6
|
+
RESERVED_NAMES = %i[resolution u frag_coord].freeze
|
|
7
|
+
|
|
6
8
|
attr_reader :uniforms
|
|
7
9
|
|
|
8
10
|
def initialize
|
|
@@ -10,7 +12,22 @@ module RLSL
|
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def define_uniform(name, type)
|
|
13
|
-
|
|
15
|
+
normalized_name = RLSL.validate_identifier!(name, context: "uniform name").to_sym
|
|
16
|
+
if RESERVED_NAMES.include?(normalized_name)
|
|
17
|
+
raise ArgumentError, "Uniform name #{normalized_name.inspect} is reserved by RLSL"
|
|
18
|
+
end
|
|
19
|
+
if @uniforms.key?(normalized_name)
|
|
20
|
+
raise ArgumentError, "Uniform #{normalized_name.inspect} is already defined"
|
|
21
|
+
end
|
|
22
|
+
generated_sampler_names = @uniforms.filter_map do |uniform_name, uniform_type|
|
|
23
|
+
:"#{uniform_name}_sampler" if uniform_type == :sampler2D
|
|
24
|
+
end
|
|
25
|
+
if generated_sampler_names.include?(normalized_name) ||
|
|
26
|
+
(type == :sampler2D && @uniforms.key?(:"#{normalized_name}_sampler"))
|
|
27
|
+
raise ArgumentError, "Uniform #{normalized_name.inspect} conflicts with a generated sampler name"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@uniforms[normalized_name] = type
|
|
14
31
|
end
|
|
15
32
|
|
|
16
33
|
RLSL::UNIFORM_TYPES.each do |type|
|
data/lib/rlsl/version.rb
CHANGED