render: Add 'naga-agal' crate to compile AGAL shaders to Naga
This is the first part of the Stage3D implementation, and can
be reviewed independently.
Stage3D shaders use the Adobe Graphics Assembly Language (AGAL),
which is a binary shader format. It supports vertex attributes,
varying registers, program constants (uniforms), and texture sampling.
This PR only implements a few parts of AGAL:
* The 'mov' and 'm44' opcodes
* Vertex attributes, varying registers, program constants, and 'output'
registers (position or color, depending on shader type)
This is sufficient to get a non-trivial Stage3D program
running (the rotating cube demo from the Adobe docs).
The output of `naga-agal` is a `naga::Module`. This can be passed
directly to wgpu, or compiled into a shader language using
a Naga backend (glsl, wgsl, SPIR-V, etc). The test suite
output WGSL files, and uses the 'insta' crate to compare against
saved files on disk.
Currently, the only real way to write AGAL bytecode is using
the Adobe-provided 'AGALMiniAssembler' flash class.
This class assembles the textual reprentation of AGAL into
the binary format.
To make writing tests easier, I've added a 'agal_compiler' test, which
can easily be modified to add more Agal textual assembly.
2022-09-23 00:44:49 +00:00
|
|
|
use naga::{
|
|
|
|
valid::{Capabilities, ValidationFlags, Validator},
|
|
|
|
Module,
|
|
|
|
};
|
|
|
|
use naga_agal::{agal_to_naga, VertexAttributeFormat};
|
|
|
|
|
|
|
|
pub fn to_wgsl(module: &Module) -> String {
|
|
|
|
let mut out = String::new();
|
|
|
|
|
|
|
|
let mut validator = Validator::new(ValidationFlags::all(), Capabilities::all());
|
|
|
|
let module_info = validator
|
|
|
|
.validate(module)
|
2022-10-26 23:46:09 +00:00
|
|
|
.unwrap_or_else(|e| panic!("Validation failed: {e}"));
|
render: Add 'naga-agal' crate to compile AGAL shaders to Naga
This is the first part of the Stage3D implementation, and can
be reviewed independently.
Stage3D shaders use the Adobe Graphics Assembly Language (AGAL),
which is a binary shader format. It supports vertex attributes,
varying registers, program constants (uniforms), and texture sampling.
This PR only implements a few parts of AGAL:
* The 'mov' and 'm44' opcodes
* Vertex attributes, varying registers, program constants, and 'output'
registers (position or color, depending on shader type)
This is sufficient to get a non-trivial Stage3D program
running (the rotating cube demo from the Adobe docs).
The output of `naga-agal` is a `naga::Module`. This can be passed
directly to wgpu, or compiled into a shader language using
a Naga backend (glsl, wgsl, SPIR-V, etc). The test suite
output WGSL files, and uses the 'insta' crate to compare against
saved files on disk.
Currently, the only real way to write AGAL bytecode is using
the Adobe-provided 'AGALMiniAssembler' flash class.
This class assembles the textual reprentation of AGAL into
the binary format.
To make writing tests easier, I've added a 'agal_compiler' test, which
can easily be modified to add more Agal textual assembly.
2022-09-23 00:44:49 +00:00
|
|
|
|
|
|
|
let mut writer =
|
|
|
|
naga::back::wgsl::Writer::new(&mut out, naga::back::wgsl::WriterFlags::EXPLICIT_TYPES);
|
|
|
|
|
|
|
|
writer.write(module, &module_info).expect("Writing failed");
|
|
|
|
out
|
|
|
|
}
|
|
|
|
|
|
|
|
// Making this a macro gives us a better span in 'inta'
|
|
|
|
macro_rules! test_shader {
|
|
|
|
($shader:expr, $attrs:expr, $shader_type:expr $(,)?) => {
|
naga-agal: Fix handling of sampler overrides
After some testing, and looking at OpenFL, I believe I've
determined the correct behavior for AGAL sampling:
Each time a Context3D.setProgram or Context3D.setSamplerStateAt
call is made, the sampler config for the used texture slot(s)
is updated with the new wrapping/filter behavior. For setProgram,
this comes from all of the 'tex' opcodes used within the program.
However, when the 'ignoresampler' flag is set in a 'tex' opcode,
the setProgram call does *not* override the existing sampler config.
As a result, that program will sample with the behavior determined
by the most recent setSamplerStateAt or setProgram call involving
the used texture slot(s).
Previously, we were always overriding the opcode sampler config
with the values from Context3D.setSamplerStateAt. However, I didn't
realize that the order of the calls matter, so none of my tests ended
up observing the effect of 'ignoresampler'.
We now need to process AGAL bytecode twice - a quick initial
parse to determine the sampler configs (which need to be updated
when we call 'setProgram'), and a second time when to build the
Naga module (which needs to wait until we have the vertex attributes
available, which can be changed by ActionScript after setting
the program).
2023-11-25 21:58:09 +00:00
|
|
|
let module = agal_to_naga(&$shader, $attrs, &[Default::default(); 8]).unwrap();
|
render: Add 'naga-agal' crate to compile AGAL shaders to Naga
This is the first part of the Stage3D implementation, and can
be reviewed independently.
Stage3D shaders use the Adobe Graphics Assembly Language (AGAL),
which is a binary shader format. It supports vertex attributes,
varying registers, program constants (uniforms), and texture sampling.
This PR only implements a few parts of AGAL:
* The 'mov' and 'm44' opcodes
* Vertex attributes, varying registers, program constants, and 'output'
registers (position or color, depending on shader type)
This is sufficient to get a non-trivial Stage3D program
running (the rotating cube demo from the Adobe docs).
The output of `naga-agal` is a `naga::Module`. This can be passed
directly to wgpu, or compiled into a shader language using
a Naga backend (glsl, wgsl, SPIR-V, etc). The test suite
output WGSL files, and uses the 'insta' crate to compare against
saved files on disk.
Currently, the only real way to write AGAL bytecode is using
the Adobe-provided 'AGALMiniAssembler' flash class.
This class assembles the textual reprentation of AGAL into
the binary format.
To make writing tests easier, I've added a 'agal_compiler' test, which
can easily be modified to add more Agal textual assembly.
2022-09-23 00:44:49 +00:00
|
|
|
let output = to_wgsl(&module);
|
2024-03-04 04:35:43 +00:00
|
|
|
insta::assert_snapshot!(output);
|
render: Add 'naga-agal' crate to compile AGAL shaders to Naga
This is the first part of the Stage3D implementation, and can
be reviewed independently.
Stage3D shaders use the Adobe Graphics Assembly Language (AGAL),
which is a binary shader format. It supports vertex attributes,
varying registers, program constants (uniforms), and texture sampling.
This PR only implements a few parts of AGAL:
* The 'mov' and 'm44' opcodes
* Vertex attributes, varying registers, program constants, and 'output'
registers (position or color, depending on shader type)
This is sufficient to get a non-trivial Stage3D program
running (the rotating cube demo from the Adobe docs).
The output of `naga-agal` is a `naga::Module`. This can be passed
directly to wgpu, or compiled into a shader language using
a Naga backend (glsl, wgsl, SPIR-V, etc). The test suite
output WGSL files, and uses the 'insta' crate to compare against
saved files on disk.
Currently, the only real way to write AGAL bytecode is using
the Adobe-provided 'AGALMiniAssembler' flash class.
This class assembles the textual reprentation of AGAL into
the binary format.
To make writing tests easier, I've added a 'agal_compiler' test, which
can easily be modified to add more Agal textual assembly.
2022-09-23 00:44:49 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_shaders() {
|
|
|
|
test_shader!(
|
|
|
|
// m44 op, va0, vc0
|
|
|
|
// mov v0, va1
|
|
|
|
[
|
|
|
|
160, 1, 0, 0, 0, 161, 0, 24, 0, 0, 0, 0, 0, 15, 3, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
228, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 4, 1, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0
|
|
|
|
],
|
|
|
|
&[
|
|
|
|
Some(VertexAttributeFormat::Float3),
|
|
|
|
Some(VertexAttributeFormat::Float3),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
],
|
|
|
|
ShaderType::Vertex,
|
|
|
|
);
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
// mov op, va0
|
|
|
|
// mov v0, va1
|
|
|
|
[
|
|
|
|
160, 1, 0, 0, 0, 161, 0, 0, 0, 0, 0, 0, 0, 15, 3, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 4, 1, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
|
|
],
|
|
|
|
&[
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
],
|
|
|
|
ShaderType::Vertex,
|
|
|
|
);
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
// mov oc, v0
|
|
|
|
[
|
|
|
|
160, 1, 0, 0, 0, 161, 1, 0, 0, 0, 0, 0, 0, 15, 3, 0, 0, 0, 228, 4, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0
|
|
|
|
],
|
|
|
|
&[None, None, None, None, None, None, None, None],
|
|
|
|
ShaderType::Fragment,
|
|
|
|
);
|
|
|
|
}
|
2022-09-24 03:14:49 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_complex_raytrace() {
|
|
|
|
const RAYTRACE_VERTEX: &[u8] = include!("raytrace_vertex.agal");
|
|
|
|
const RAYTRACE_FRAGMENT: &[u8] = include!("raytrace_fragment.agal");
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
RAYTRACE_VERTEX,
|
|
|
|
&[
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None
|
|
|
|
],
|
|
|
|
ShaderType::Vertex
|
|
|
|
);
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
RAYTRACE_FRAGMENT,
|
|
|
|
&[None, None, None, None, None, None, None, None],
|
|
|
|
ShaderType::Fragment
|
|
|
|
);
|
|
|
|
}
|
2023-04-04 00:10:49 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_complex_fractal() {
|
|
|
|
const FRACTAL_VERTEX: &[u8] = include!("fractal_vertex.agal");
|
|
|
|
const FRACTAL_FRAGMENT: &[u8] = include!("fractal_fragment.agal");
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
FRACTAL_VERTEX,
|
|
|
|
&[
|
|
|
|
Some(VertexAttributeFormat::Float2),
|
|
|
|
Some(VertexAttributeFormat::Float2),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None
|
|
|
|
],
|
|
|
|
ShaderType::Vertex
|
|
|
|
);
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
FRACTAL_FRAGMENT,
|
|
|
|
&[None, None, None, None, None, None, None, None],
|
|
|
|
ShaderType::Fragment
|
|
|
|
);
|
|
|
|
}
|
2023-05-14 03:32:07 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_relative_load() {
|
|
|
|
// mov vt0, vc[va0.x+5]
|
|
|
|
// mov vt1, vc[va1.y+6]
|
|
|
|
// add op, vt0, vt1
|
|
|
|
const RELATIVE_VERTEX: &[u8] = include!("relative_vertex.agal");
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
RELATIVE_VERTEX,
|
|
|
|
&[
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None
|
|
|
|
],
|
|
|
|
ShaderType::Vertex
|
|
|
|
);
|
|
|
|
}
|
2023-05-18 16:53:59 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_misc_opcodes() {
|
|
|
|
// log vt0, va0
|
|
|
|
// exp vt1, vt0
|
|
|
|
// pow vt2, vt1, va0
|
|
|
|
// sge vt3, vt2, va0
|
|
|
|
// m33 vt4, vc0, vt3
|
|
|
|
// m34 vt5, vc2, vt3
|
|
|
|
// min vt6, vt5, vt4
|
|
|
|
// rsq op, vt6
|
|
|
|
const MISC_OPCODES_VERTEX: &[u8] = include!("misc_opcodes_vertex.agal");
|
|
|
|
|
|
|
|
// ddx ft0, v0
|
|
|
|
// ddy ft1, ft0
|
|
|
|
// kil ft1.x
|
|
|
|
// mov oc, ft0
|
|
|
|
const MISC_OPCODES_FRAGMENT: &[u8] = include!("misc_opcodes_fragment.agal");
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
MISC_OPCODES_VERTEX,
|
|
|
|
&[
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
Some(VertexAttributeFormat::Float4),
|
|
|
|
],
|
|
|
|
ShaderType::Vertex
|
|
|
|
);
|
|
|
|
|
|
|
|
test_shader!(
|
|
|
|
MISC_OPCODES_FRAGMENT,
|
|
|
|
&[None, None, None, None, None, None, None, None],
|
|
|
|
ShaderType::Fragment
|
|
|
|
);
|
|
|
|
}
|