shader: Add Metal tests

Fixes #1340
This commit is contained in:
Hajime Hoshi 2020-09-10 01:11:15 +09:00
parent 031f8376e9
commit 337f44c916
5 changed files with 132 additions and 34 deletions

View File

@ -29,13 +29,41 @@ import (
"github.com/hajimehoshi/ebiten/internal/shaderir/metal" "github.com/hajimehoshi/ebiten/internal/shaderir/metal"
) )
func normalize(str string) string { func glslNormalize(str string) string {
if strings.HasPrefix(str, glsl.FragmentPrelude) { if strings.HasPrefix(str, glsl.FragmentPrelude) {
str = str[len(glsl.FragmentPrelude):] str = str[len(glsl.FragmentPrelude):]
} }
return strings.TrimSpace(str) return strings.TrimSpace(str)
} }
func metalNormalize(str string) string {
if strings.HasPrefix(str, metal.Prelude) {
str = str[len(metal.Prelude):]
}
return strings.TrimSpace(str)
}
func compare(t *testing.T, title, got, want string) {
var msg string
gotlines := strings.Split(got, "\n")
wantlines := strings.Split(want, "\n")
for i := range gotlines {
if len(wantlines) <= i {
msg = fmt.Sprintf(`lines %d:
got: %s
want: (out of range)`, i+1, gotlines[i])
break
}
if gotlines[i] != wantlines[i] {
msg = fmt.Sprintf(`lines %d:
got: %s
want: %s`, i+1, gotlines[i], wantlines[i])
break
}
}
t.Errorf("%s: got: %v, want: %v\n\n%s", title, got, want, msg)
}
func TestCompile(t *testing.T) { func TestCompile(t *testing.T) {
if runtime.GOOS == "js" { if runtime.GOOS == "js" {
t.Skip("file open might not be implemented in this environment") t.Skip("file open might not be implemented in this environment")
@ -51,6 +79,7 @@ func TestCompile(t *testing.T) {
Src []byte Src []byte
VS []byte VS []byte
FS []byte FS []byte
Metal []byte
} }
fnames := map[string]struct{}{} fnames := map[string]struct{}{}
@ -100,6 +129,15 @@ func TestCompile(t *testing.T) {
t.Fatalf("no expected file for %s", name) t.Fatalf("no expected file for %s", name)
} }
metaln := name + ".expected.metal"
if _, ok := fnames[metaln]; ok {
metal, err := ioutil.ReadFile(filepath.Join("testdata", metaln))
if err != nil {
t.Fatal(err)
}
tc.Metal = metal
}
tests = append(tests, tc) tests = append(tests, tc)
} }
@ -116,30 +154,22 @@ func TestCompile(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
// GLSL
vs, fs := glsl.Compile(s) vs, fs := glsl.Compile(s)
if got, want := normalize(vs), normalize(string(tc.VS)); got != want { if got, want := glslNormalize(vs), glslNormalize(string(tc.VS)); got != want {
var msg string compare(t, "GLSL Vertex", got, want)
gotlines := strings.Split(got, "\n")
wantlines := strings.Split(want, "\n")
for i := range gotlines {
if len(wantlines) <= i {
msg = fmt.Sprintf(`lines %d:
got: %s
want: (out of range)`, i+1, gotlines[i])
break
}
if gotlines[i] != wantlines[i] {
msg = fmt.Sprintf(`lines %d:
got: %s
want: %s`, i+1, gotlines[i], wantlines[i])
break
}
}
t.Errorf("got: %v, want: %v\n\n%s", got, want, msg)
} }
if tc.FS != nil { if tc.FS != nil {
if got, want := normalize(fs), normalize(string(tc.FS)); got != want { if got, want := glslNormalize(fs), glslNormalize(string(tc.FS)); got != want {
t.Errorf("got: %v, want: %v", got, want) compare(t, "GLSL Fragment", got, want)
}
}
if tc.Metal != nil {
m := metal.Compile(s, "Vertex", "Fragment")
if got, want := metalNormalize(m), metalNormalize(string(tc.Metal)); got != want {
compare(t, "Metal", got, want)
} }
} }

View File

@ -0,0 +1,12 @@
void F0(thread array<float2, 3>& l0);
void F0(thread array<float2, 3>& l0) {
array<float2, 2> l1 = {};
array<float2, 3> l2 = {};
{
array<float2, 2> l2 = {};
l2 = l1;
}
l0 = l2;
return;
}

View File

@ -0,0 +1,24 @@
struct Attributes {
packed_float2 M0;
packed_float2 M1;
packed_float4 M2;
};
struct Varyings {
float4 Position [[position]];
float2 M0;
float4 M1;
};
vertex Varyings Vertex(
uint vid [[vertex_id]],
const device Attributes* attributes [[buffer(0)]],
constant float2& U0 [[buffer(1)]]) {
Varyings varyings = {};
float4x4 l0 = float4x4(0);
l0 = float4x4((2.0) / ((U0).x), 0.0, 0.0, 0.0, 0.0, (2.0) / ((U0).y), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0);
varyings.Position = (l0) * (float4(attributes[vid].M0, 0.0, 1.0));
varyings.M0 = attributes[vid].M1;
varyings.M1 = attributes[vid].M2;
return varyings;
}

View File

@ -0,0 +1,32 @@
struct Attributes {
packed_float2 M0;
packed_float2 M1;
packed_float4 M2;
};
struct Varyings {
float4 Position [[position]];
float2 M0;
float4 M1;
};
vertex Varyings Vertex(
uint vid [[vertex_id]],
const device Attributes* attributes [[buffer(0)]],
constant float2& U0 [[buffer(1)]]) {
Varyings varyings = {};
float4x4 l0 = float4x4(0);
l0 = float4x4((2.0) / ((U0).x), 0.0, 0.0, 0.0, 0.0, (2.0) / ((U0).y), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0);
varyings.Position = (l0) * (float4(attributes[vid].M0, 0.0, 1.0));
varyings.M0 = attributes[vid].M1;
varyings.M1 = attributes[vid].M2;
return varyings;
}
fragment float4 Fragment(
Varyings varyings [[stage_in]],
constant float2& U0 [[buffer(1)]]) {
float4 out = float4(0);
out = float4((varyings.Position).x, (varyings.M0).y, (varyings.M1).z, 1.0);
return out;
}

View File

@ -48,20 +48,20 @@ func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) strin
return n return n
} }
const Prelude = `#include <metal_stdlib>
using namespace metal;
constexpr sampler texture_sampler{filter::nearest};`
func Compile(p *shaderir.Program, vertex, fragment string) (shader string) { func Compile(p *shaderir.Program, vertex, fragment string) (shader string) {
c := &compileContext{ c := &compileContext{
structNames: map[string]string{}, structNames: map[string]string{},
} }
var lines []string var lines []string
lines = append(lines, lines = append(lines, strings.Split(Prelude, "\n")...)
"#include <metal_stdlib>", lines = append(lines, "", "{{.Structs}}")
"",
"using namespace metal;",
"",
"{{.Structs}}",
"",
"constexpr sampler texture_sampler{filter::nearest};")
if len(p.Attributes) > 0 { if len(p.Attributes) > 0 {
lines = append(lines, "") lines = append(lines, "")