rlsl 0.1.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +18 -1
- data/lib/rlsl/base_translator/call_parser.rb +68 -0
- data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
- data/lib/rlsl/base_translator/code_scanner.rb +102 -0
- data/lib/rlsl/base_translator.rb +159 -63
- data/lib/rlsl/code_generator/math_prelude.rb +79 -0
- data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +97 -0
- data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
- data/lib/rlsl/code_generator/template_context.rb +41 -0
- data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
- data/lib/rlsl/code_generator.rb +26 -201
- data/lib/rlsl/compiled_shader.rb +4 -12
- data/lib/rlsl/function_context.rb +31 -14
- data/lib/rlsl/glsl/translator.rb +19 -38
- data/lib/rlsl/msl/shader.rb +8 -38
- data/lib/rlsl/msl/translator.rb +19 -36
- data/lib/rlsl/msl/uniform_buffer_packer.rb +68 -0
- data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +96 -0
- data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +79 -0
- data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +168 -0
- data/lib/rlsl/prism/ast_visitor/scope_context.rb +44 -0
- data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
- data/lib/rlsl/prism/ast_visitor.rb +54 -298
- data/lib/rlsl/prism/builtins/function_registry.rb +114 -0
- data/lib/rlsl/prism/builtins/operator_rules.rb +99 -0
- data/lib/rlsl/prism/builtins/swizzle_rules.rb +38 -0
- data/lib/rlsl/prism/builtins.rb +31 -148
- data/lib/rlsl/prism/compilation_unit.rb +7 -0
- data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +83 -0
- data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +104 -0
- data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +92 -0
- data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +89 -0
- data/lib/rlsl/prism/emitters/base_emitter.rb +68 -423
- data/lib/rlsl/prism/emitters/c_emitter.rb +78 -121
- data/lib/rlsl/prism/emitters/glsl_emitter.rb +30 -65
- data/lib/rlsl/prism/emitters/msl_emitter.rb +35 -62
- data/lib/rlsl/prism/emitters/target_emitter.rb +77 -0
- data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
- data/lib/rlsl/prism/emitters/wgsl_emitter.rb +65 -61
- data/lib/rlsl/prism/ir/control_flow.rb +83 -0
- data/lib/rlsl/prism/ir/definitions.rb +67 -0
- data/lib/rlsl/prism/ir/expressions.rb +197 -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 +62 -0
- data/lib/rlsl/prism/source_extractor/block_locator.rb +52 -0
- data/lib/rlsl/prism/source_extractor.rb +14 -133
- data/lib/rlsl/prism/source_unit/parser.rb +78 -0
- data/lib/rlsl/prism/source_unit.rb +42 -0
- data/lib/rlsl/prism/target_capability_validator.rb +85 -0
- data/lib/rlsl/prism/transpiler.rb +63 -60
- data/lib/rlsl/prism/type_inference/call_type_resolver.rb +35 -0
- data/lib/rlsl/prism/type_inference/call_validator.rb +71 -0
- data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +80 -0
- data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +66 -0
- data/lib/rlsl/prism/type_inference/definition_inferer.rb +41 -0
- data/lib/rlsl/prism/type_inference/expression_inferer.rb +92 -0
- data/lib/rlsl/prism/type_inference/field_type_resolver.rb +17 -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 +114 -248
- data/lib/rlsl/runtime_shader.rb +47 -0
- data/lib/rlsl/shader_builder/build_service.rb +77 -0
- data/lib/rlsl/shader_builder/native_extension_compiler.rb +71 -0
- data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
- data/lib/rlsl/shader_builder/source_resolver.rb +91 -0
- data/lib/rlsl/shader_builder.rb +22 -113
- 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 +81 -0
- data/lib/rlsl/types.rb +11 -29
- data/lib/rlsl/uniform_context.rb +6 -12
- data/lib/rlsl/version.rb +1 -1
- data/lib/rlsl/wgsl/translator.rb +23 -39
- data/lib/rlsl.rb +14 -12
- metadata +55 -16
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
int idx = (y * width + 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
|
+
static VALUE shader_#{@context.name}_render(VALUE self, VALUE rb_buffer, VALUE rb_width, VALUE rb_height#{@context.uniform_argument_list}) {
|
|
33
|
+
int width = NUM2INT(rb_width);
|
|
34
|
+
int height = NUM2INT(rb_height);
|
|
35
|
+
vec2 resolution = vec2_new((float)width, (float)height);
|
|
36
|
+
|
|
37
|
+
Uniforms uniforms;
|
|
38
|
+
#{uniform_assignments}
|
|
39
|
+
|
|
40
|
+
Check_Type(rb_buffer, T_STRING);
|
|
41
|
+
rb_str_modify(rb_buffer);
|
|
42
|
+
uint8_t *pixels = (uint8_t *)RSTRING_PTR(rb_buffer);
|
|
43
|
+
|
|
44
|
+
#ifdef __APPLE__
|
|
45
|
+
dispatch_apply(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t y) {
|
|
46
|
+
render_scanline(pixels, width, height, (int)y, resolution, uniforms);
|
|
47
|
+
});
|
|
48
|
+
#else
|
|
49
|
+
for (int y = 0; y < height; y++) {
|
|
50
|
+
render_scanline(pixels, width, height, y, resolution, uniforms);
|
|
51
|
+
}
|
|
52
|
+
#endif
|
|
53
|
+
|
|
54
|
+
return Qnil;
|
|
55
|
+
}
|
|
56
|
+
C
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def uniform_assignments
|
|
62
|
+
@context.uniform_entries.map do |uniform_name, spec|
|
|
63
|
+
generate_uniform_assignment(uniform_name, spec)
|
|
64
|
+
end.join("\n")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def generate_uniform_assignment(uniform_name, spec)
|
|
68
|
+
case spec.wrapper_kind
|
|
69
|
+
when :float
|
|
70
|
+
" uniforms.#{uniform_name} = (float)NUM2DBL(rb_#{uniform_name});"
|
|
71
|
+
when :int
|
|
72
|
+
" uniforms.#{uniform_name} = NUM2INT(rb_#{uniform_name});"
|
|
73
|
+
when :bool
|
|
74
|
+
" uniforms.#{uniform_name} = RTEST(rb_#{uniform_name}) ? 1 : 0;"
|
|
75
|
+
when :vector
|
|
76
|
+
generate_vector_uniform_assignment(uniform_name, spec.vector_size)
|
|
77
|
+
else
|
|
78
|
+
raise ArgumentError, "Unsupported compiled uniform type: #{spec.c_type}"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def generate_vector_uniform_assignment(uniform_name, vector_size)
|
|
83
|
+
constructor = VECTOR_CONSTRUCTORS.fetch(vector_size)
|
|
84
|
+
components = (0...vector_size).map do |index|
|
|
85
|
+
" (float)NUM2DBL(rb_ary_entry(rb_#{uniform_name}, #{index}))"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
<<~C.strip
|
|
89
|
+
Check_Type(rb_#{uniform_name}, T_ARRAY);
|
|
90
|
+
uniforms.#{uniform_name} = #{constructor}(
|
|
91
|
+
#{components.join(",\n")}
|
|
92
|
+
);
|
|
93
|
+
C
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
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,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
class CodeGenerator
|
|
5
|
+
class TemplateContext
|
|
6
|
+
attr_reader :name
|
|
7
|
+
|
|
8
|
+
def initialize(name:, uniforms:, helpers_block:, fragment_block:)
|
|
9
|
+
@name = name
|
|
10
|
+
@uniforms = uniforms
|
|
11
|
+
@helpers_block = helpers_block
|
|
12
|
+
@fragment_block = fragment_block
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def render_arity
|
|
16
|
+
3 + @uniforms.size
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def helpers_code
|
|
20
|
+
return "" unless @helpers_block
|
|
21
|
+
|
|
22
|
+
@helpers_block.call
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def fragment_code
|
|
26
|
+
@fragment_block.call
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def uniform_entries
|
|
30
|
+
@uniform_entries ||= @uniforms.map do |uniform_name, type|
|
|
31
|
+
[uniform_name, UniformTypes.compiled_spec(type)]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def uniform_argument_list
|
|
36
|
+
arguments = @uniforms.map { |uniform_name, _| "VALUE rb_#{uniform_name}" }
|
|
37
|
+
arguments.empty? ? "" : ", #{arguments.join(', ')}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -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 {} 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,12 +1,20 @@
|
|
|
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
11
|
def initialize(name, uniforms, helpers_block, fragment_block)
|
|
6
|
-
@
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
@context = TemplateContext.new(
|
|
13
|
+
name: name,
|
|
14
|
+
uniforms: uniforms,
|
|
15
|
+
helpers_block: helpers_block,
|
|
16
|
+
fragment_block: fragment_block
|
|
17
|
+
)
|
|
10
18
|
end
|
|
11
19
|
|
|
12
20
|
def generate
|
|
@@ -18,212 +26,29 @@ module RLSL
|
|
|
18
26
|
#include <dispatch/dispatch.h>
|
|
19
27
|
#endif
|
|
20
28
|
|
|
21
|
-
#{
|
|
22
|
-
#{
|
|
23
|
-
#{
|
|
24
|
-
#{
|
|
25
|
-
#{
|
|
26
|
-
#{
|
|
29
|
+
#{RLSL::C_TYPES}
|
|
30
|
+
#{MathPrelude.code}
|
|
31
|
+
#{UniformStructGenerator.new(@context).generate}
|
|
32
|
+
#{custom_helpers}
|
|
33
|
+
#{ShaderFunctionGenerator.new(@context).generate}
|
|
34
|
+
#{RubyWrapperGenerator.new(@context).generate}
|
|
27
35
|
|
|
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
|
-
}
|
|
36
|
+
#{init_function}
|
|
33
37
|
C
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
private
|
|
37
41
|
|
|
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
|
|
42
|
+
def custom_helpers
|
|
43
|
+
@context.helpers_code
|
|
141
44
|
end
|
|
142
45
|
|
|
143
|
-
def
|
|
144
|
-
shader_body = @fragment_block.call
|
|
145
|
-
|
|
46
|
+
def init_function
|
|
146
47
|
<<~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;
|
|
48
|
+
void Init_#{@context.name}(void) {
|
|
49
|
+
VALUE mRLSL = rb_define_module("RLSL");
|
|
50
|
+
VALUE mShaders = rb_define_module_under(mRLSL, "CompiledShaders");
|
|
51
|
+
rb_define_module_function(mShaders, "#{@context.name}_render", shader_#{@context.name}_render, #{@context.render_arity});
|
|
227
52
|
}
|
|
228
53
|
C
|
|
229
54
|
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
|
-
@uniform_names = uniform_names
|
|
9
8
|
@render_method = RLSL::CompiledShaders.method("#{name}_render")
|
|
10
9
|
end
|
|
11
10
|
|
|
12
|
-
def metal?
|
|
13
|
-
false
|
|
14
|
-
end
|
|
15
|
-
|
|
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
|
|
@@ -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,17 +3,15 @@
|
|
|
3
3
|
module RLSL
|
|
4
4
|
module GLSL
|
|
5
5
|
class Translator < BaseTranslator
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
target_vec4: "vec4"
|
|
16
|
-
).freeze
|
|
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
|
+
)
|
|
14
|
+
)
|
|
17
15
|
|
|
18
16
|
def initialize(uniforms, helpers_code, fragment_code, version: "450")
|
|
19
17
|
super(uniforms, helpers_code, fragment_code)
|
|
@@ -22,15 +20,6 @@ module RLSL
|
|
|
22
20
|
|
|
23
21
|
protected
|
|
24
22
|
|
|
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
23
|
def generate_shader(helpers, fragment)
|
|
35
24
|
<<~GLSL
|
|
36
25
|
#version #{@version}
|
|
@@ -70,28 +59,20 @@ module RLSL
|
|
|
70
59
|
|
|
71
60
|
private
|
|
72
61
|
|
|
62
|
+
def profile
|
|
63
|
+
PROFILE
|
|
64
|
+
end
|
|
65
|
+
|
|
73
66
|
def generate_uniform_declarations
|
|
74
|
-
declarations = ["layout(binding = 1) uniform ShaderUniforms {"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
declarations = ["layout(binding = 1) uniform ShaderUniforms {"]
|
|
68
|
+
declarations.concat(
|
|
69
|
+
uniform_lines(resolution_line: " #{target_vec2_type} resolution;") do |name, glsl_type|
|
|
70
|
+
" #{glsl_type} #{name};"
|
|
71
|
+
end
|
|
72
|
+
)
|
|
80
73
|
declarations << "} u;"
|
|
81
74
|
declarations.join("\n")
|
|
82
75
|
end
|
|
83
|
-
|
|
84
|
-
def target_vec2_type
|
|
85
|
-
"vec2"
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def target_vec3_type
|
|
89
|
-
"vec3"
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def target_vec4_type
|
|
93
|
-
"vec4"
|
|
94
|
-
end
|
|
95
76
|
end
|
|
96
77
|
end
|
|
97
78
|
end
|
data/lib/rlsl/msl/shader.rb
CHANGED
|
@@ -9,21 +9,24 @@ end
|
|
|
9
9
|
|
|
10
10
|
module RLSL
|
|
11
11
|
module MSL
|
|
12
|
-
class Shader
|
|
12
|
+
class Shader < RuntimeShader
|
|
13
13
|
attr_reader :name, :msl_source
|
|
14
14
|
|
|
15
15
|
def initialize(name, uniforms, msl_source)
|
|
16
|
-
|
|
17
|
-
@uniform_types = uniforms
|
|
18
|
-
@uniform_names = uniforms.keys
|
|
16
|
+
super(name, uniforms)
|
|
19
17
|
@msl_source = msl_source
|
|
20
18
|
@compiled_handles = {}
|
|
19
|
+
@uniform_buffer_packer = UniformBufferPacker.new(@name, @uniform_types, @uniform_names)
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
def metal?
|
|
24
23
|
true
|
|
25
24
|
end
|
|
26
25
|
|
|
26
|
+
def render(handle, width, height, uniforms = {})
|
|
27
|
+
render_metal(handle, width, height, uniforms)
|
|
28
|
+
end
|
|
29
|
+
|
|
27
30
|
def render_metal(handle, width, height, uniforms = {})
|
|
28
31
|
unless METACO_AVAILABLE
|
|
29
32
|
raise LoadError, "metaco gem is required for Metal rendering. Install it with: gem install metaco"
|
|
@@ -43,40 +46,7 @@ module RLSL
|
|
|
43
46
|
private
|
|
44
47
|
|
|
45
48
|
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")
|
|
49
|
+
@uniform_buffer_packer.pack(width, height, uniforms)
|
|
80
50
|
end
|
|
81
51
|
end
|
|
82
52
|
end
|