shader: Implement vertex shader entry point

This commit is contained in:
Hajime Hoshi 2020-06-02 22:45:44 +09:00
parent fa5b2ed730
commit cd3d396975
4 changed files with 171 additions and 8 deletions

View File

@ -25,6 +25,10 @@ import (
"github.com/hajimehoshi/ebiten/internal/shaderir" "github.com/hajimehoshi/ebiten/internal/shaderir"
) )
const (
vertexEntry = "Vertex"
)
type variable struct { type variable struct {
name string name string
typ typ typ typ
@ -52,11 +56,26 @@ type compileState struct {
// uniforms is a collection of uniform variable names. // uniforms is a collection of uniform variable names.
uniforms []string uniforms []string
// attributes is a collection of attribute variable names.
attributes []string
// varyings is a collection of varying variable names.
varyings []string
global block global block
errs []string errs []string
} }
func (cs *compileState) findUniformVariable(name string) (int, bool) {
for i, u := range cs.uniforms {
if u == name {
return i, true
}
}
return 0, false
}
type block struct { type block struct {
types []typ types []typ
vars []variable vars []variable
@ -178,7 +197,12 @@ func (cs *compileState) parseDecl(b *block, d ast.Decl) {
cs.addError(d.Pos(), "unexpected token") cs.addError(d.Pos(), "unexpected token")
} }
case *ast.FuncDecl: case *ast.FuncDecl:
b.funcs = append(b.funcs, cs.parseFunc(d, b)) f := cs.parseFunc(b, d)
if b == &cs.global && d.Name.Name == vertexEntry {
cs.ir.VertexFunc.Block = f.ir.Block
} else {
b.funcs = append(b.funcs, f)
}
default: default:
cs.addError(d.Pos(), "unexpected decl") cs.addError(d.Pos(), "unexpected decl")
} }
@ -230,7 +254,7 @@ func (s *compileState) parseConstant(vs *ast.ValueSpec) []constant {
return cs return cs
} }
func (cs *compileState) parseFunc(d *ast.FuncDecl, block *block) function { func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) function {
if d.Name == nil { if d.Name == nil {
cs.addError(d.Pos(), "function must have a name") cs.addError(d.Pos(), "function must have a name")
return function{} return function{}
@ -242,6 +266,7 @@ func (cs *compileState) parseFunc(d *ast.FuncDecl, block *block) function {
var inT []shaderir.Type var inT []shaderir.Type
var inParams []variable var inParams []variable
for _, f := range d.Type.Params.List { for _, f := range d.Type.Params.List {
t := cs.parseType(f.Type) t := cs.parseType(f.Type)
for _, n := range f.Names { for _, n := range f.Names {
@ -255,6 +280,7 @@ func (cs *compileState) parseFunc(d *ast.FuncDecl, block *block) function {
var outT []shaderir.Type var outT []shaderir.Type
var outParams []variable var outParams []variable
if d.Type.Results != nil { if d.Type.Results != nil {
for _, f := range d.Type.Results.List { for _, f := range d.Type.Results.List {
t := cs.parseType(f.Type) t := cs.parseType(f.Type)
@ -276,6 +302,32 @@ func (cs *compileState) parseFunc(d *ast.FuncDecl, block *block) function {
} }
} }
if block == &cs.global && d.Name.Name == vertexEntry {
for _, v := range inParams {
cs.attributes = append(cs.attributes, v.name)
}
for _, t := range inT {
cs.ir.Attributes = append(cs.ir.Attributes, t)
}
// The first out-param is treated as gl_Position in GLSL.
if len(outParams) == 0 {
cs.addError(d.Pos(), fmt.Sprintf("vertex entry point must have at least one return vec4 value for a positoin"))
return function{}
}
if outT[0].Main != shaderir.Vec4 {
cs.addError(d.Pos(), fmt.Sprintf("vertex entry point must have at least one return vec4 value for a positoin"))
return function{}
}
for _, v := range outParams[1:] {
cs.varyings = append(cs.varyings, v.name)
}
for _, t := range outT[1:] {
cs.ir.Varyings = append(cs.ir.Varyings, t)
}
}
b := cs.parseBlock(block, d.Body, inParams, outParams) b := cs.parseBlock(block, d.Body, inParams, outParams)
return function{ return function{
@ -458,6 +510,59 @@ func (cs *compileState) parseExpr(block *block, expr ast.Expr) shaderir.Expr {
default: default:
cs.addError(e.Pos(), fmt.Sprintf("literal not implemented: %#v", e)) cs.addError(e.Pos(), fmt.Sprintf("literal not implemented: %#v", e))
} }
case *ast.BinaryExpr:
var op shaderir.Op
switch e.Op {
case token.ADD:
op = shaderir.Add
case token.SUB:
op = shaderir.Sub
case token.NOT:
op = shaderir.NotOp
case token.MUL:
op = shaderir.Mul
case token.QUO:
op = shaderir.Div
case token.REM:
op = shaderir.ModOp
case token.SHL:
op = shaderir.LeftShift
case token.SHR:
op = shaderir.RightShift
case token.LSS:
op = shaderir.LessThanOp
case token.LEQ:
op = shaderir.LessThanEqualOp
case token.GTR:
op = shaderir.GreaterThanOp
case token.GEQ:
op = shaderir.GreaterThanEqualOp
case token.EQL:
op = shaderir.EqualOp
case token.NEQ:
op = shaderir.NotEqualOp
case token.AND:
op = shaderir.And
case token.XOR:
op = shaderir.Xor
case token.OR:
op = shaderir.Or
case token.LAND:
op = shaderir.AndAnd
case token.LOR:
op = shaderir.OrOr
default:
cs.addError(e.Pos(), fmt.Sprintf("unexpected operator: %s", e.Op))
return shaderir.Expr{}
}
return shaderir.Expr{
Type: shaderir.Binary,
Op: op,
Exprs: []shaderir.Expr{
cs.parseExpr(block, e.X),
cs.parseExpr(block, e.Y),
},
}
case *ast.CallExpr: case *ast.CallExpr:
exprs := []shaderir.Expr{ exprs := []shaderir.Expr{
cs.parseExpr(block, e.Fun), cs.parseExpr(block, e.Fun),
@ -472,15 +577,19 @@ func (cs *compileState) parseExpr(block *block, expr ast.Expr) shaderir.Expr {
Exprs: exprs, Exprs: exprs,
} }
case *ast.Ident: case *ast.Ident:
i, ok := block.findLocalVariable(e.Name) if i, ok := block.findLocalVariable(e.Name); ok {
if ok {
return shaderir.Expr{ return shaderir.Expr{
Type: shaderir.LocalVariable, Type: shaderir.LocalVariable,
Index: i, Index: i,
} }
} }
f, ok := shaderir.ParseBuiltinFunc(e.Name) if i, ok := cs.findUniformVariable(e.Name); ok {
if ok { return shaderir.Expr{
Type: shaderir.UniformVariable,
Index: i,
}
}
if f, ok := shaderir.ParseBuiltinFunc(e.Name); ok {
return shaderir.Expr{ return shaderir.Expr{
Type: shaderir.BuiltinFuncExpr, Type: shaderir.BuiltinFuncExpr,
BuiltinFunc: f, BuiltinFunc: f,
@ -498,6 +607,26 @@ func (cs *compileState) parseExpr(block *block, expr ast.Expr) shaderir.Expr {
}, },
}, },
} }
case *ast.UnaryExpr:
var op shaderir.Op
switch e.Op {
case token.ADD:
op = shaderir.Add
case token.SUB:
op = shaderir.Sub
case token.NOT:
op = shaderir.NotOp
default:
cs.addError(e.Pos(), fmt.Sprintf("unexpected operator: %s", e.Op))
return shaderir.Expr{}
}
return shaderir.Expr{
Type: shaderir.Unary,
Op: op,
Exprs: []shaderir.Expr{
cs.parseExpr(block, e.X),
},
}
default: default:
cs.addError(e.Pos(), fmt.Sprintf("expression not implemented: %#v", e)) cs.addError(e.Pos(), fmt.Sprintf("expression not implemented: %#v", e))
} }

View File

@ -129,6 +129,37 @@ func Foo(foo vec2) vec4 {
l2 = vec4(l0, 0.0, 1.0); l2 = vec4(l0, 0.0, 1.0);
l1 = l2; l1 = l2;
return; return;
}`,
},
{
Name: "vertex",
Src: `package main
var ScreenSize vec2
func Vertex(position vec2, texCoord vec2, color vec4) (position vec4, texCoord vec2, color vec4) {
projectionMatrix := mat4(
2 / ScreenSize.x, 0, 0, 0,
0, 2 / ScreenSize.y, 0, 0,
0, 0, 1, 0,
-1, -1, 0, 1,
)
return projectionMatrix * vec4(position, 0, 1), texCoord, color
}`,
VS: `uniform vec2 U0;
attribute vec2 A0;
attribute vec2 A1;
attribute vec4 A2;
varying vec2 V0;
varying vec4 V1;
void main(void) {
mat4 l0 = mat4(0.0);
l0 = mat4((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);
gl_Position = (l0) * (vec4(A0, 0.0, 1.0));
V0 = A1;
V1 = A2;
return;
}`, }`,
}, },
} }

View File

@ -86,6 +86,9 @@ func (p *Program) Glsl() (vertexShader, fragmentShader string) {
for i, t := range p.Varyings { for i, t := range p.Varyings {
vslines = append(vslines, fmt.Sprintf("varying %s;", p.glslVarDecl(&t, fmt.Sprintf("V%d", i)))) vslines = append(vslines, fmt.Sprintf("varying %s;", p.glslVarDecl(&t, fmt.Sprintf("V%d", i))))
} }
if len(vslines) > 0 && len(p.Funcs) > 0 {
vslines = append(vslines, "")
}
for _, f := range p.Funcs { for _, f := range p.Funcs {
vslines = append(vslines, p.glslFunc(&f)...) vslines = append(vslines, p.glslFunc(&f)...)
} }
@ -285,7 +288,7 @@ func (p *Program) glslBlock(b *Block, level int, localVarIndex int) []string {
case Unary: case Unary:
var op string var op string
switch e.Op { switch e.Op {
case Add, Sub, Neg: case Add, Sub, NotOp:
op = string(e.Op) op = string(e.Op)
default: default:
op = fmt.Sprintf("?(unexpected op: %s)", string(e.Op)) op = fmt.Sprintf("?(unexpected op: %s)", string(e.Op))

View File

@ -116,7 +116,7 @@ type Op string
const ( const (
Add Op = "+" Add Op = "+"
Sub Op = "-" Sub Op = "-"
Neg Op = "!" NotOp Op = "!"
Mul Op = "*" Mul Op = "*"
Div Op = "/" Div Op = "/"
ModOp Op = "%" ModOp Op = "%"