electra 0.1.0 → 0.1.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/CHANGELOG.md +4 -0
- data/README.md +122 -49
- data/lib/electra/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c323b1caf054026764f6565a277a638961d9e490cd6929eb043000602520c9b
|
|
4
|
+
data.tar.gz: 76d8b64b72db2d6fb1c3e999470df1ae371962cc15bd461ccdd0d3a0588e690e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 976cdfa357990c76839dd0d890f005ffbb7f15442564fb5bc02beae1b3e560955757f4c7d1343dbab96eec03a59d89ecebc63b7e4631de6bc0da86bdb9051609
|
|
7
|
+
data.tar.gz: d994a50d51031e0db82a566d2af2adf8b084f16244b315fff44482473763f20cec73a000906276b8ee65c1bbbc11661b5ccb815de580aa5792c7bc6f35253f6a
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -1,50 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<h1 align="center">Electra</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>Pure Ruby SPIR-V emitter, typed shader DSL, assembler, and disassembler</strong>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://rubygems.org/gems/electra"><img src="https://img.shields.io/gem/v/electra.svg" alt="Gem version"></a>
|
|
9
|
+
<a href="https://rubygems.org/gems/electra"><img src="https://img.shields.io/gem/dt/electra.svg" alt="Gem downloads"></a>
|
|
10
|
+
<img src="https://img.shields.io/badge/ruby-%3E%3D%203.1-ruby.svg" alt="Ruby version">
|
|
11
|
+
<a href="https://github.com/noxdea/electra/actions/workflows/main.yml"><img src="https://github.com/noxdea/electra/actions/workflows/main.yml/badge.svg" alt="CI status"></a>
|
|
12
|
+
<a href="LICENSE.txt"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT license"></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="#features">Features</a> ·
|
|
17
|
+
<a href="#installation">Installation</a> ·
|
|
18
|
+
<a href="#quick-start">Quick Start</a> ·
|
|
19
|
+
<a href="#shader-dsl">Shader DSL</a> ·
|
|
20
|
+
<a href="#assembly-and-low-level-api">Assembly</a> ·
|
|
21
|
+
<a href="#development">Development</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
4
25
|
|
|
5
26
|
Electra builds the small shaders a UI renderer needs directly from Ruby, without running GLSL compilers or loading native bindings. Use it when a Vulkan application needs to ship shader construction as Ruby source. It is not a GLSL compiler, optimizer, or complete SPIR-V semantic validator.
|
|
6
27
|
|
|
7
|
-
|
|
28
|
+

|
|
8
29
|
|
|
9
|
-
##
|
|
30
|
+
## Features
|
|
10
31
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
32
|
+
- Typed vertex and fragment shader DSL for UI renderers
|
|
33
|
+
- Pure Ruby with no runtime gem dependencies
|
|
34
|
+
- SPIR-V 1.0–1.6 binary emission
|
|
35
|
+
- Numeric-ID assembler and disassembler with byte-for-byte round trips
|
|
36
|
+
- Explicit uniform, push-constant, array, and matrix layouts
|
|
37
|
+
- Grammar tables derived from a pinned Khronos SPIRV-Headers revision
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Add Electra to your bundle:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
bundle add electra
|
|
17
45
|
```
|
|
18
46
|
|
|
19
|
-
|
|
47
|
+
Or install it directly:
|
|
20
48
|
|
|
21
49
|
```sh
|
|
22
50
|
gem install electra
|
|
23
51
|
```
|
|
24
52
|
|
|
25
|
-
|
|
53
|
+
### Requirements
|
|
54
|
+
|
|
55
|
+
- Ruby 3.1 or newer
|
|
56
|
+
- SPIRV-Tools is optional and only used for development validation
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
Build a fragment shader and inspect its SPIR-V assembly:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
require "electra"
|
|
64
|
+
|
|
65
|
+
mod = Electra::Module.new
|
|
66
|
+
mod.fragment_shader do |f|
|
|
67
|
+
output = f.output(:vec4, location: 0)
|
|
68
|
+
output.store(f.constant([1, 0, 0, 1]))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
binary = mod.to_binary
|
|
72
|
+
puts Electra.disassemble(binary)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Generate the bundled triangle and text shader examples from a checkout:
|
|
26
76
|
|
|
27
77
|
```sh
|
|
28
|
-
gem build electra.gemspec
|
|
29
|
-
gem install --local electra-0.1.0.gem
|
|
30
78
|
ruby -Ilib examples/example_shaders.rb tmp/shaders
|
|
31
79
|
```
|
|
32
80
|
|
|
33
|
-
The
|
|
81
|
+
The triangle vertex shader uses `VertexIndex`, so the consumer can issue `vkCmdDraw(3, 1, 0, 0)` without a vertex buffer. Both entry points are named `main`; the fragment output is location 0.
|
|
34
82
|
|
|
35
|
-
##
|
|
83
|
+
## Shader DSL
|
|
84
|
+
|
|
85
|
+
This texture shader samples an alpha channel and multiplies it by a vertex tint:
|
|
36
86
|
|
|
37
87
|
```ruby
|
|
38
88
|
mod = Electra::Module.new(version: "1.0", generator: 0)
|
|
39
|
-
mod.capability :Shader
|
|
40
|
-
mod.memory_model :Logical, :GLSL450
|
|
41
89
|
|
|
42
90
|
mod.fragment_shader("main") do |f|
|
|
43
91
|
uv = f.input(:vec2, location: 0)
|
|
44
92
|
tint = f.input(:vec4, location: 1)
|
|
45
|
-
|
|
93
|
+
texture = f.sampled_image_2d(set: 0, binding: 0)
|
|
46
94
|
output = f.output(:vec4, location: 0)
|
|
47
|
-
alpha = f.sample(
|
|
95
|
+
alpha = f.sample(texture, uv).component(:r)
|
|
48
96
|
output.store(f.mul(tint, f.splat(alpha)))
|
|
49
97
|
end
|
|
50
98
|
|
|
@@ -52,20 +100,20 @@ binary = mod.to_binary
|
|
|
52
100
|
raise "round trip failed" unless Electra.assemble(Electra.disassemble(binary)) == binary
|
|
53
101
|
```
|
|
54
102
|
|
|
55
|
-
`Shader`, `Logical/GLSL450`, `OriginUpperLeft` for fragments, and function return instructions are added automatically.
|
|
103
|
+
`Shader`, `Logical/GLSL450`, `OriginUpperLeft` for fragments, and function return instructions are added automatically. `to_binary` returns a little-endian binary String with an exact ID bound and schema zero. The application must select a SPIR-V version supported by its Vulkan environment. SPIR-V 1.4+ descriptor and push-constant variables are included in entry-point interfaces.
|
|
56
104
|
|
|
57
|
-
|
|
105
|
+
### Types and operations
|
|
58
106
|
|
|
59
|
-
Scalar types are `:bool`, `:int`, `:uint`, `:float` (32-bit numbers). Vectors are `:vec2`–`:vec4`,
|
|
107
|
+
Scalar types are `:bool`, `:int`, `:uint`, and `:float` (32-bit numbers). Vectors are `:vec2`–`:vec4`, `:ivecN`, `:uvecN`, and `:bvecN`. Matrices `:mat2`–`:mat4` and `:matCxR` are column-major floating-point matrices. `mod.array(type, count, stride:)` and `mod.struct(*types, offsets:, block:)` expose explicit aggregate layout. Struct and matrix constants use nested arrays, one array per column.
|
|
60
108
|
|
|
61
|
-
Types are structurally interned; constants are interned by
|
|
109
|
+
Types are structurally interned; constants are interned by type and exact bit pattern, including negative zero. `f.constant(value, type)` creates a typed constant. Numeric literals passed alongside a typed operand use its type; use `1.0` for an inferred float and `1` for an inferred signed integer.
|
|
62
110
|
|
|
63
111
|
| Operation | API |
|
|
64
112
|
| --- | --- |
|
|
65
113
|
| Inputs/outputs | `input(type, location:)`, `output(type, location:)`; alternatively `builtin: :Position` / `:VertexIndex`; input `flat: true` |
|
|
66
114
|
| Components/composites | `value.component(:r)` / `component(0)`, `construct(type, *components)`, `splat(scalar, count = 4)` |
|
|
67
115
|
| Arithmetic | `add`, `sub`, `mul`, `div`, `mod`, `negate`; scalar/vector numeric types |
|
|
68
|
-
| Matrices | `mul` selects matrix×matrix, matrix×vector, vector×matrix or scalar multiplication; `transpose`, `dot` |
|
|
116
|
+
| Matrices | `mul` selects matrix×matrix, matrix×vector, vector×matrix, or scalar multiplication; `transpose`, `dot` |
|
|
69
117
|
| Conversion | `convert(value, type)` for numeric scalar/vector conversion; int/uint conversion is bit-preserving |
|
|
70
118
|
| Comparison | `equal`, `not_equal`, `less_than`, `less_equal`, `greater_than`, `greater_equal` |
|
|
71
119
|
| Boolean/selection | `logical_and`, `logical_or`, `logical_not`, `select(condition, yes, no)` |
|
|
@@ -75,6 +123,8 @@ Types are structurally interned; constants are interned by their type and exact
|
|
|
75
123
|
|
|
76
124
|
Inputs and descriptors load automatically when consumed; output variables use `store`. Types and values cannot cross module boundaries, and function-local values cannot cross function boundaries. Integer fragment inputs should be declared `flat: true` as required by Vulkan.
|
|
77
125
|
|
|
126
|
+
### Structured control flow
|
|
127
|
+
|
|
78
128
|
Structured branches allocate forward labels and merge matching returned values with `OpPhi`:
|
|
79
129
|
|
|
80
130
|
```ruby
|
|
@@ -86,45 +136,58 @@ adjusted = f.if_else(f.less_than(alpha, 0.5),
|
|
|
86
136
|
f.output(:vec4, location: 0).store(f.splat(adjusted))
|
|
87
137
|
```
|
|
88
138
|
|
|
89
|
-
|
|
139
|
+
### Uniform and push-constant layout
|
|
90
140
|
|
|
91
|
-
Offsets
|
|
141
|
+
Offsets and strides are explicit: the emitter does not guess a CPU/GPU ABI.
|
|
92
142
|
|
|
93
143
|
```ruby
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
144
|
+
mod.vertex_shader do |f|
|
|
145
|
+
input = f.input(:vec4, location: 0)
|
|
146
|
+
output = f.output(:vec4, builtin: :Position)
|
|
147
|
+
buffer = f.uniform_buffer(:mat4, set: 0, binding: 0, offsets: [0])
|
|
148
|
+
push = f.push_constant(:vec4, offsets: [0])
|
|
149
|
+
|
|
150
|
+
mod.member_decorate(buffer.type, 0, :ColMajor)
|
|
151
|
+
mod.member_decorate(buffer.type, 0, :MatrixStride, 16)
|
|
152
|
+
output.store(f.add(f.mul(f.member(buffer, 0), input), f.member(push, 0)))
|
|
153
|
+
end
|
|
99
154
|
```
|
|
100
155
|
|
|
101
|
-
`decorate(target, decoration, *values)` supports the grammar's decorations, including Location, Binding, DescriptorSet, Block and ArrayStride. `member_decorate(type, index, decoration, *values)` handles Offset and matrix layout. Repeating the same decoration is harmless; conflicting values raise `Electra::Error`.
|
|
156
|
+
`decorate(target, decoration, *values)` supports the grammar's decorations, including Location, Binding, DescriptorSet, Block, and ArrayStride. `member_decorate(type, index, decoration, *values)` handles Offset and matrix layout. Repeating the same decoration is harmless; conflicting values raise `Electra::Error`.
|
|
157
|
+
|
|
158
|
+
## Assembly and low-level API
|
|
159
|
+
|
|
160
|
+
`Electra.disassemble(binary)` validates the header, instruction framing, operands, strings, and ID bounds. `Electra.assemble(text)` accepts numeric `%123` IDs and SPIR-V instruction syntax. Output from the disassembler round-trips byte-for-byte, preserving version, generator, bound, and endianness through header comments. Floating-point hexadecimal spelling preserves 16/32/64-bit constants, signed zero, and NaN payloads.
|
|
102
161
|
|
|
103
|
-
For instructions outside the DSL, `mod.reserve_id` / `mod.ref(:label)` reserve forward IDs, and `mod.emit(section, "OpName", id, "name")` accepts grammar-order operands. Sections are emitted in specification order
|
|
162
|
+
For instructions outside the DSL, `mod.reserve_id` / `mod.ref(:label)` reserve forward IDs, and `mod.emit(section, "OpName", id, "name")` accepts grammar-order operands. Sections are emitted in specification order. Every reserved ID must be defined exactly once. The low-level API does not validate dominance, capability requirements, or storage-layout rules: run `spirv-val`.
|
|
104
163
|
|
|
105
|
-
|
|
164
|
+
The opcode, enum, and GLSL.std.450 tables are derived from a pinned [Khronos SPIRV-Headers revision](https://github.com/KhronosGroup/SPIRV-Headers/tree/496543121ce6419f23d6fa5d7194ba66c36212d2/include/spirv/unified1).
|
|
106
165
|
|
|
107
|
-
|
|
166
|
+
### Supported scope and limits
|
|
108
167
|
|
|
109
|
-
The
|
|
168
|
+
The typed DSL intentionally covers UI vertex and fragment shaders only. Compute, tessellation, geometry, subgroups, ray tracing, GLSL parsing, optimization, and GPU resource management are not implemented.
|
|
110
169
|
|
|
111
|
-
|
|
170
|
+
The assembler and disassembler are not a general replacement for SPIRV-Tools. Nonnumeric ID names, other extended-instruction symbolic vocabularies, and context-dependent 64-bit `OpSwitch` literals are outside the supported round-trip surface. No binary is claimed valid merely because it can be disassembled; the Vulkan implementation and, during development, `spirv-val` check full shader semantics and GPU limits.
|
|
112
171
|
|
|
113
|
-
Module
|
|
172
|
+
Errors are reported as `Electra::Error`; discard a builder after a failed mutation. Module and function objects own mutable construction state, so use one builder per thread. Type and value records and generated grammar tables are frozen. Independent builders share no allocation counters or shader state.
|
|
114
173
|
|
|
115
|
-
##
|
|
174
|
+
## Development
|
|
116
175
|
|
|
117
176
|
```sh
|
|
118
177
|
bundle install
|
|
119
|
-
bundle exec rake
|
|
178
|
+
bundle exec rake # tests and isolated install
|
|
120
179
|
bundle exec rake test:oracle # requires SPIRV-Tools; skips if absent
|
|
121
|
-
BUDGET=1 bundle exec rake bench
|
|
180
|
+
BUDGET=1 bundle exec rake bench # 10 ms regression guard
|
|
122
181
|
rbs -I sig validate
|
|
123
182
|
```
|
|
124
183
|
|
|
125
|
-
|
|
184
|
+
Tests cover malformed and truncated binaries, 1,000 seeded binary mutations, type and constant interning, forward references, exact assembly round trips, float boundaries, and strings. When installed, `spirv-val`, `spirv-dis`, and `spirv-as` validate complete emitted instruction streams across SPIR-V 1.0, 1.3, 1.4, 1.5, and 1.6. Linux CI requires these external checks; macOS and Windows skip them when the tools are unavailable.
|
|
185
|
+
|
|
186
|
+
A Vulkan draw/readback test belongs to the renderer consuming the shared triangle shaders. SPIRV-Tools tests alone do not prove a rendered image.
|
|
187
|
+
|
|
188
|
+
### Performance
|
|
126
189
|
|
|
127
|
-
Measured locally on arm64 macOS
|
|
190
|
+
Measured locally on arm64 macOS with Ruby 4.0.0 and YJIT; 604-byte text fragment, median of five runs of 1,000 operations:
|
|
128
191
|
|
|
129
192
|
| Workload | Time | Allocated objects |
|
|
130
193
|
| --- | ---: | ---: |
|
|
@@ -132,10 +195,20 @@ Measured locally on arm64 macOS, Ruby 4.0.0 + YJIT; 604-byte text fragment, medi
|
|
|
132
195
|
| Disassemble | 108.82 µs | 959 |
|
|
133
196
|
| Assemble | 205.05 µs | 1,653 |
|
|
134
197
|
|
|
135
|
-
Shaders are intended to be built once and retained, not rebuilt per frame. Rerun `bench/emitter.rb` on the target Ruby
|
|
198
|
+
Shaders are intended to be built once and retained, not rebuilt per frame. Rerun `bench/emitter.rb` on the target Ruby and platform; these are measurements, not universal guarantees.
|
|
199
|
+
|
|
200
|
+
## Contributing
|
|
201
|
+
|
|
202
|
+
Bug reports and pull requests are welcome at <https://github.com/noxdea/electra>.
|
|
203
|
+
|
|
204
|
+
## Name
|
|
205
|
+
|
|
206
|
+
Electra is a named star in the Pleiades. This library uses the name for its
|
|
207
|
+
precise SPIR-V instruction engraving. The RubyGems name is `electra`; the Ruby
|
|
208
|
+
namespace is `Electra`.
|
|
136
209
|
|
|
137
|
-
##
|
|
210
|
+
## License
|
|
138
211
|
|
|
139
|
-
|
|
212
|
+
Electra is released under the [MIT License](LICENSE.txt). Khronos grammar and derived tables retain their [upstream license](LICENSE-SPIRV-Headers.txt).
|
|
140
213
|
|
|
141
|
-
|
|
214
|
+
Protocol references: [SPIR-V registry](https://registry.khronos.org/SPIR-V/), [Vulkan SPIR-V environment](https://docs.vulkan.org/spec/latest/appendices/spirvenv.html), and [GLSL.std.450](https://registry.khronos.org/SPIR-V/specs/unified1/GLSL.std.450.html).
|
data/lib/electra/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: electra
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yudai Takada
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-09-21 00:00:00.000000000 Z
|
|
11
12
|
dependencies: []
|
|
13
|
+
description:
|
|
12
14
|
email:
|
|
13
15
|
- t.yudai92@gmail.com
|
|
14
16
|
executables: []
|
|
@@ -38,6 +40,7 @@ metadata:
|
|
|
38
40
|
changelog_uri: https://github.com/noxdea/electra/blob/main/CHANGELOG.md
|
|
39
41
|
allowed_push_host: https://rubygems.org
|
|
40
42
|
rubygems_mfa_required: 'true'
|
|
43
|
+
post_install_message:
|
|
41
44
|
rdoc_options: []
|
|
42
45
|
require_paths:
|
|
43
46
|
- lib
|
|
@@ -52,7 +55,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
52
55
|
- !ruby/object:Gem::Version
|
|
53
56
|
version: '0'
|
|
54
57
|
requirements: []
|
|
55
|
-
rubygems_version: 4.
|
|
58
|
+
rubygems_version: 3.4.19
|
|
59
|
+
signing_key:
|
|
56
60
|
specification_version: 4
|
|
57
61
|
summary: A pure Ruby SPIR-V shader emitter and disassembler
|
|
58
62
|
test_files: []
|