diff --git a/internal/shaderir/glsl.go b/internal/shaderir/glsl.go new file mode 100644 index 000000000..6f976bb66 --- /dev/null +++ b/internal/shaderir/glsl.go @@ -0,0 +1,71 @@ +// Copyright 2020 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shaderir + +import ( + "fmt" + "strings" +) + +func (p *Program) structName(t *Type) string { + if t.MainType != Struct { + panic("shaderir: the given type at structName must be a struct") + } + s := t.serialize() + if n, ok := p.structNames[s]; ok { + return n + } + n := fmt.Sprintf("S%d", len(p.structNames)) + p.structNames[s] = n + p.structTypes = append(p.structTypes, *t) + return n +} + +func (p *Program) Glsl() string { + p.structNames = map[string]string{} + p.structTypes = nil + + var lines []string + for _, u := range p.Uniforms { + lines = append(lines, fmt.Sprintf("uniform %s;", p.glslVarDecl(&u.Type, u.Name))) + } + + var stLines []string + for i, t := range p.structTypes { + stLines = append(stLines, fmt.Sprintf("struct S%d {", i)) + for j, st := range t.SubTypes { + stLines = append(stLines, fmt.Sprintf("\t%s;", p.glslVarDecl(&st, fmt.Sprintf("M%d", j)))) + } + stLines = append(stLines, "};") + } + lines = append(stLines, lines...) + + return strings.Join(lines, "\n") + "\n" +} + +func (p *Program) glslVarDecl(t *Type, varname string) string { + switch t.MainType { + case None: + return "?(none)" + case Image2D: + panic("not implemented") + case Array: + return fmt.Sprintf("") + case Struct: + return fmt.Sprintf("%s %s", p.structName(t), varname) + default: + return fmt.Sprintf("%s %s", t.MainType.Glsl(), varname) + } +} diff --git a/internal/shaderir/ir_test.go b/internal/shaderir/ir_test.go new file mode 100644 index 000000000..beac69b4e --- /dev/null +++ b/internal/shaderir/ir_test.go @@ -0,0 +1,74 @@ +// Copyright 2020 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shaderir_test + +import ( + "testing" + + . "github.com/hajimehoshi/ebiten/internal/shaderir" +) + +func TestOutput(t *testing.T) { + tests := []struct { + Name string + Program Program + Glsl string + }{ + { + Name: "Empty", + Program: Program{}, + Glsl: ``, + }, + { + Name: "Uniform", + Program: Program{ + Uniforms: []Variable{ + { + Name: "U1", + Type: Type{MainType: Float}, + }, + }, + }, + Glsl: `uniform float U1;`, + }, + { + Name: "UniformStruct", + Program: Program{ + Uniforms: []Variable{ + { + Name: "U1", + Type: Type{ + MainType: Struct, + SubTypes: []Type{ + {MainType: Float}, + }, + }, + }, + }, + }, + Glsl: `struct S0 { + float M0; +}; +uniform S0 U1;`, + }, + } + for _, tc := range tests { + got := tc.Program.Glsl() + want := tc.Glsl + "\n" + if got != want { + t.Errorf("%s: got: %s, want: %s", tc.Name, got, want) + } + } +} diff --git a/internal/shaderir/program.go b/internal/shaderir/program.go new file mode 100644 index 000000000..7176a22e7 --- /dev/null +++ b/internal/shaderir/program.go @@ -0,0 +1,111 @@ +// Copyright 2020 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shaderir + +type Program struct { + Uniforms []Variable + Attributes []Variable + Varyings []Variable + Funcs []Func + VertexEntry string + FragmentEntry string + + structNames map[string]string + structTypes []Type +} + +type Variable struct { + Name string + Type Type +} + +type Func struct { + Name string + InParams []Type + OutParams []Type + Block Block +} + +type Block struct { + LocalVars []Type + Stmts []Stmt +} + +type Stmt struct { + Type StmtType + Exprs []Expr + Condition Expr + ElseStmts []Expr + ForInit Expr + ForRest Expr +} + +type StmtType int + +const ( + ExprStmt StmtType = iota + Assign + If + For + Continue + Break + Return + Discard +) + +type Expr struct { + Type ExprType + Exprs []Expr + Value string + Op Op +} + +type ExprType int + +const ( + Literal ExprType = iota + Ident + LocalVarID + Unary + Binary + Call + Selector + Index +) + +type Op string + +const ( + Add Op = "+" + Sub Op = "-" + Neg Op = "!" + Mul Op = "*" + Div Op = "/" + Mod Op = "%" + LShift Op = "<<" + RShift Op = ">>" + LT Op = "<" + LE Op = "<=" + GT Op = ">" + GE Op = ">=" + Eq Op = "==" + NE Op = "!=" + And Op = "&" + Xor Op = "^" + Or Op = "|" + AndAnd Op = "&&" + OrOr Op = "||" + Cond Op = "?:" +) diff --git a/internal/shaderir/type.go b/internal/shaderir/type.go new file mode 100644 index 000000000..5faaf623e --- /dev/null +++ b/internal/shaderir/type.go @@ -0,0 +1,113 @@ +// Copyright 2020 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shaderir + +import ( + "fmt" + "strings" +) + +type Type struct { + MainType BasicType + SubTypes []Type + Length int +} + +func (t *Type) serialize() string { + switch t.MainType { + case None: + return "none" + case Bool: + return "bool" + case Float: + return "float" + case Vec2: + return "vec2" + case Vec3: + return "vec3" + case Vec4: + return "vec4" + case Mat2: + return "mat2" + case Mat3: + return "mat3" + case Mat4: + return "mat4" + case Image2D: + return "image2d" + case Array: + return fmt.Sprintf("%s[%d]", t.SubTypes[0].serialize(), t.Length) + case Struct: + str := "struct{" + sub := make([]string, 0, len(t.SubTypes)) + for _, st := range t.SubTypes { + sub = append(sub, st.serialize()) + } + str += strings.Join(sub, ",") + str += "}" + return str + default: + return fmt.Sprintf("?(unknown type: %d)", t) + } +} + +type BasicType int + +const ( + None BasicType = iota + Bool + Float + Vec2 + Vec3 + Vec4 + Mat2 + Mat3 + Mat4 + Image2D + Array + Struct +) + +func (t BasicType) Glsl() string { + switch t { + case None: + return "?(none)" + case Bool: + return "bool" + case Float: + return "float" + case Vec2: + return "vec2" + case Vec3: + return "vec3" + case Vec4: + return "vec4" + case Mat2: + return "mat2" + case Mat3: + return "mat3" + case Mat4: + return "mat4" + case Image2D: + return "?(image2d)" + case Array: + // First-class array is not available on GLSL ES 2. + return "?(array)" + case Struct: + return "?(struct)" + default: + return fmt.Sprintf("?(unknown type: %d)", t) + } +}