shader: Implement an array variable

Updates #1235
This commit is contained in:
Hajime Hoshi 2020-07-29 00:57:47 +09:00
parent c032d61f93
commit d9c54bc0d0
6 changed files with 66 additions and 22 deletions

View File

@ -211,7 +211,7 @@ func (cs *compileState) parse(f *ast.File) {
continue continue
} }
inParams, outParams := cs.parseFuncParams(fd) inParams, outParams := cs.parseFuncParams(&cs.global, fd)
var inT, outT []shaderir.Type var inT, outT []shaderir.Type
for _, v := range inParams { for _, v := range inParams {
inT = append(inT, v.typ) inT = append(inT, v.typ)
@ -260,7 +260,7 @@ func (cs *compileState) parseDecl(b *block, d ast.Decl) ([]shaderir.Stmt, bool)
// TODO: Parse other types // TODO: Parse other types
for _, s := range d.Specs { for _, s := range d.Specs {
s := s.(*ast.TypeSpec) s := s.(*ast.TypeSpec)
t := cs.parseType(s.Type) t := cs.parseType(b, s.Type)
b.types = append(b.types, typ{ b.types = append(b.types, typ{
name: s.Name.Name, name: s.Name.Name,
ir: t, ir: t,
@ -269,7 +269,7 @@ func (cs *compileState) parseDecl(b *block, d ast.Decl) ([]shaderir.Stmt, bool)
case token.CONST: case token.CONST:
for _, s := range d.Specs { for _, s := range d.Specs {
s := s.(*ast.ValueSpec) s := s.(*ast.ValueSpec)
cs := cs.parseConstant(s) cs := cs.parseConstant(b, s)
b.consts = append(b.consts, cs...) b.consts = append(b.consts, cs...)
} }
case token.VAR: case token.VAR:
@ -385,7 +385,7 @@ func (s *compileState) parseVariable(block *block, vs *ast.ValueSpec) ([]variabl
var declt shaderir.Type var declt shaderir.Type
if vs.Type != nil { if vs.Type != nil {
declt = s.parseType(vs.Type) declt = s.parseType(block, vs.Type)
} }
var ( var (
@ -480,10 +480,10 @@ func (s *compileState) parseVariable(block *block, vs *ast.ValueSpec) ([]variabl
return vars, inits, stmts, true return vars, inits, stmts, true
} }
func (s *compileState) parseConstant(vs *ast.ValueSpec) []constant { func (s *compileState) parseConstant(block *block, vs *ast.ValueSpec) []constant {
var t shaderir.Type var t shaderir.Type
if vs.Type != nil { if vs.Type != nil {
t = s.parseType(vs.Type) t = s.parseType(block, vs.Type)
} }
var cs []constant var cs []constant
@ -497,9 +497,9 @@ func (s *compileState) parseConstant(vs *ast.ValueSpec) []constant {
return cs return cs
} }
func (cs *compileState) parseFuncParams(d *ast.FuncDecl) (in, out []variable) { func (cs *compileState) parseFuncParams(block *block, d *ast.FuncDecl) (in, out []variable) {
for _, f := range d.Type.Params.List { for _, f := range d.Type.Params.List {
t := cs.parseType(f.Type) t := cs.parseType(block, f.Type)
for _, n := range f.Names { for _, n := range f.Names {
in = append(in, variable{ in = append(in, variable{
name: n.Name, name: n.Name,
@ -513,7 +513,7 @@ func (cs *compileState) parseFuncParams(d *ast.FuncDecl) (in, out []variable) {
} }
for _, f := range d.Type.Results.List { for _, f := range d.Type.Results.List {
t := cs.parseType(f.Type) t := cs.parseType(block, f.Type)
if len(f.Names) == 0 { if len(f.Names) == 0 {
out = append(out, variable{ out = append(out, variable{
name: "", name: "",
@ -541,7 +541,7 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool
return function{}, false return function{}, false
} }
inParams, outParams := cs.parseFuncParams(d) inParams, outParams := cs.parseFuncParams(block, d)
checkVaryings := func(vs []variable) { checkVaryings := func(vs []variable) {
if len(cs.ir.Varyings) != len(vs) { if len(cs.ir.Varyings) != len(vs) {

View File

@ -0,0 +1 @@
uniform vec2[4] U0;

3
internal/shader/testdata/array.go vendored Normal file
View File

@ -0,0 +1,3 @@
package main
var Array [4]vec2

View File

@ -17,13 +17,12 @@ package shader
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
gconstant "go/constant"
"github.com/hajimehoshi/ebiten/internal/shaderir" "github.com/hajimehoshi/ebiten/internal/shaderir"
) )
// TODO: What about array types? func (cs *compileState) parseType(block *block, expr ast.Expr) shaderir.Type {
func (cs *compileState) parseType(expr ast.Expr) shaderir.Type {
switch t := expr.(type) { switch t := expr.(type) {
case *ast.Ident: case *ast.Ident:
switch t.Name { switch t.Name {
@ -49,6 +48,41 @@ func (cs *compileState) parseType(expr ast.Expr) shaderir.Type {
cs.addError(t.Pos(), fmt.Sprintf("unexpected type: %s", t.Name)) cs.addError(t.Pos(), fmt.Sprintf("unexpected type: %s", t.Name))
return shaderir.Type{} return shaderir.Type{}
} }
case *ast.ArrayType:
if t.Len == nil {
cs.addError(t.Pos(), fmt.Sprintf("array length must be specified"))
return shaderir.Type{}
}
// TODO: Parse ellipsis
exprs, _, _, ok := cs.parseExpr(block, t.Len)
if !ok {
return shaderir.Type{}
}
if len(exprs) != 1 {
cs.addError(t.Pos(), fmt.Sprintf("invalid length of array"))
return shaderir.Type{}
}
if exprs[0].Type != shaderir.NumberExpr {
cs.addError(t.Pos(), fmt.Sprintf("length of array must be a constant number"))
return shaderir.Type{}
}
len, ok := gconstant.Int64Val(exprs[0].Const)
if !ok {
cs.addError(t.Pos(), fmt.Sprintf("length of array must be an integer"))
return shaderir.Type{}
}
elm := cs.parseType(block, t.Elt)
if elm.Main == shaderir.Array {
cs.addError(t.Pos(), fmt.Sprintf("array of array is forbidden"))
return shaderir.Type{}
}
return shaderir.Type{
Main: shaderir.Array,
Sub: []shaderir.Type{elm},
Length: int(len),
}
case *ast.StructType: case *ast.StructType:
cs.addError(t.Pos(), "struct is not implemented") cs.addError(t.Pos(), "struct is not implemented")
return shaderir.Type{} return shaderir.Type{}

View File

@ -172,12 +172,10 @@ func (p *Program) glslType(t *Type) string {
switch t.Main { switch t.Main {
case None: case None:
return "void" return "void"
case Array:
panic("not implemented")
case Struct: case Struct:
return p.structName(t) return p.structName(t)
default: default:
return t.Main.Glsl() return t.Glsl()
} }
} }
@ -185,12 +183,10 @@ func (p *Program) glslVarDecl(t *Type, varname string) string {
switch t.Main { switch t.Main {
case None: case None:
return "?(none)" return "?(none)"
case Array:
panic("not implemented")
case Struct: case Struct:
return fmt.Sprintf("%s %s", p.structName(t), varname) return fmt.Sprintf("%s %s", p.structName(t), varname)
default: default:
return fmt.Sprintf("%s %s", t.Main.Glsl(), varname) return fmt.Sprintf("%s %s", t.Glsl(), varname)
} }
} }
@ -432,7 +428,7 @@ func (p *Program) glslBlock(topBlock, block *Block, level int, localVarIndex int
op = fmt.Sprintf("?(unexpected op: %s)", string(s.ForOp)) op = fmt.Sprintf("?(unexpected op: %s)", string(s.ForOp))
} }
t := s.ForVarType.Main t := s.ForVarType
init := constantToNumberLiteral(ct, s.ForInit) init := constantToNumberLiteral(ct, s.ForInit)
end := constantToNumberLiteral(ct, s.ForEnd) end := constantToNumberLiteral(ct, s.ForEnd)
lines = append(lines, fmt.Sprintf("%sfor (%s %s = %s; %s %s %s; %s) {", idt, t.Glsl(), v, init, v, op, end, delta)) lines = append(lines, fmt.Sprintf("%sfor (%s %s = %s; %s %s %s; %s) {", idt, t.Glsl(), v, init, v, op, end, delta))

View File

@ -85,6 +85,17 @@ func (t *Type) serialize() string {
return t.String() return t.String()
} }
func (t *Type) Glsl() string {
switch t.Main {
case Array:
return fmt.Sprintf("%s[%d]", t.Sub[0].Glsl(), t.Length)
case Struct:
panic("shaderir: a struct is not implemented")
default:
return t.Main.glsl()
}
}
type BasicType int type BasicType int
const ( const (
@ -102,7 +113,7 @@ const (
Struct Struct
) )
func (t BasicType) Glsl() string { func (t BasicType) glsl() string {
switch t { switch t {
case None: case None:
return "?(none)" return "?(none)"
@ -125,7 +136,6 @@ func (t BasicType) Glsl() string {
case Mat4: case Mat4:
return "mat4" return "mat4"
case Array: case Array:
// First-class array is not available on GLSL ES 2.
return "?(array)" return "?(array)"
case Struct: case Struct:
return "?(struct)" return "?(struct)"