updated ebiten version from 2.7.9 to 2.9.9

This commit is contained in:
2026-06-15 19:06:55 +02:00
parent 21edbc41c4
commit db1b625069
405 changed files with 31913 additions and 12595 deletions
+137 -43
View File
@@ -88,6 +88,10 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
return nil, nil, nil, false
}
stmts = append(stmts, ss...)
if len(ts) == 0 {
cs.addError(e.Pos(), fmt.Sprintf("unexpected binary operator: %s", e.Y))
return nil, nil, nil, false
}
rhst := ts[0]
op := e.Op
@@ -199,7 +203,7 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
return nil, nil, nil, false
}
for _, expr := range es {
if expr.Type == shaderir.FunctionExpr {
if expr.Type == shaderir.FunctionExpr || expr.Type == shaderir.BuiltinFuncExpr {
cs.addError(e.Pos(), fmt.Sprintf("function name cannot be an argument: %s", e.Fun))
return nil, nil, nil, false
}
@@ -417,6 +421,16 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
return nil, nil, nil, false
}
finalType = shaderir.Type{Main: shaderir.Vec4}
case shaderir.FrontFacing:
if len(args) != 0 {
cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 0 but %d", callee.BuiltinFunc, len(args)))
return nil, nil, nil, false
}
if fname != cs.fragmentEntry {
cs.addError(e.Pos(), fmt.Sprintf("frontfacing is available only in %s", cs.fragmentEntry))
return nil, nil, nil, false
}
finalType = shaderir.Type{Main: shaderir.Bool}
case shaderir.DiscardF:
if len(args) != 0 {
cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 0 but %d", callee.BuiltinFunc, len(args)))
@@ -430,7 +444,6 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
Type: shaderir.Discard,
})
return nil, nil, stmts, true
case shaderir.Clamp, shaderir.Mix, shaderir.Smoothstep, shaderir.Faceforward, shaderir.Refract:
// 3 arguments
if len(args) != 3 {
@@ -571,22 +584,65 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
finalType = argts[0]
}
case shaderir.Atan2, shaderir.Pow, shaderir.Mod, shaderir.Min, shaderir.Max, shaderir.Step, shaderir.Distance, shaderir.Dot, shaderir.Cross, shaderir.Reflect:
// 2 arguments
if len(args) != 2 {
cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 2 but %d", callee.BuiltinFunc, len(args)))
case shaderir.Min, shaderir.Max:
if len(args) < 1 {
cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 1 or more but %d", callee.BuiltinFunc, len(args)))
return nil, nil, nil, false
}
switch callee.BuiltinFunc {
case shaderir.Min, shaderir.Max:
var hasVectors bool
var hasScalars bool
for i := range argts {
if argts[i].IsFloatVector() || argts[i].IsIntVector() {
hasVectors = true
continue
}
if argts[i].Main == shaderir.Float || argts[i].Main == shaderir.Int || argts[i].Main == shaderir.None {
hasScalars = true
continue
}
}
// Resolve the constant values.
if hasVectors && hasScalars {
// Vector values and scalar values are mixed. Resolve the constant kind based on the first vector argument.
if argts[0].IsIntVector() {
for i := 1; i < len(args); i++ {
if args[i].Const != nil {
v := gconstant.ToInt(args[i].Const)
if v.Kind() == gconstant.Unknown {
cs.addError(e.Pos(), fmt.Sprintf("cannot convert %s to type int", args[i].Const.String()))
return nil, nil, nil, false
}
args[i].Const = v
argts[i] = shaderir.Type{Main: shaderir.Int}
}
}
} else if argts[0].IsFloatVector() {
for i := 1; i < len(args); i++ {
if args[i].Const != nil {
v := gconstant.ToFloat(args[i].Const)
if v.Kind() == gconstant.Unknown {
cs.addError(e.Pos(), fmt.Sprintf("cannot convert %s to type float", args[i].Const.String()))
return nil, nil, nil, false
}
args[i].Const = v
argts[i] = shaderir.Type{Main: shaderir.Float}
}
}
} else {
cs.addError(e.Pos(), fmt.Sprintf("cannot use %s as vecN or ivecN value in argument to %s", argts[0].String(), callee.BuiltinFunc))
return nil, nil, nil, false
}
} else {
// Vector values and scalar values are not mixed. Resolve the constant kind based on all the arguments.
if kind, _ := resolveConstKind(args, argts); kind != gconstant.Unknown {
switch kind {
case gconstant.Int:
for i, arg := range args {
if arg.Const == nil {
if argts[i].Main != shaderir.Int {
cs.addError(e.Pos(), fmt.Sprintf("%s's arguments don't match: %s and %s", callee.BuiltinFunc, argts[0].String(), argts[1].String()))
cs.addError(e.Pos(), fmt.Sprintf("%s's arguments must all be compatible types but got %s", callee.BuiltinFunc, argts[i].String()))
return nil, nil, nil, false
}
continue
@@ -603,7 +659,7 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
for i, arg := range args {
if arg.Const == nil {
if argts[i].Main != shaderir.Float {
cs.addError(e.Pos(), fmt.Sprintf("%s's arguments don't match: %s and %s", callee.BuiltinFunc, argts[0].String(), argts[1].String()))
cs.addError(e.Pos(), fmt.Sprintf("%s's arguments must all be compatible types but got %s", callee.BuiltinFunc, argts[i].String()))
return nil, nil, nil, false
}
continue
@@ -618,25 +674,45 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
}
}
}
}
if argts[0].IsIntVector() && args[1].Const != nil {
v := gconstant.ToInt(args[1].Const)
if v.Kind() == gconstant.Unknown {
cs.addError(e.Pos(), fmt.Sprintf("cannot convert %s to type int", args[1].Const.String()))
return nil, nil, nil, false
}
args[1].Const = v
argts[1] = shaderir.Type{Main: shaderir.Int}
for i := range args {
if argts[i].Main != shaderir.Float && !argts[i].IsFloatVector() && argts[i].Main != shaderir.Int && !argts[i].IsIntVector() {
cs.addError(e.Pos(), fmt.Sprintf("cannot use %s as float, vecN, int, or ivecN value in argument to %s", argts[i].String(), callee.BuiltinFunc))
return nil, nil, nil, false
}
if argts[0].IsFloatVector() && args[1].Const != nil {
v := gconstant.ToFloat(args[1].Const)
if v.Kind() == gconstant.Unknown {
cs.addError(e.Pos(), fmt.Sprintf("cannot convert %s to type float", args[1].Const.String()))
return nil, nil, nil, false
}
if len(args) > 1 {
baseType := argts[0]
for i := 1; i < len(args); i++ {
if len(args) == 2 {
if !baseType.Equal(&argts[i]) {
// Only allow vector + scalar (not scalar + vector)
if !((baseType.IsFloatVector() && argts[i].Main == shaderir.Float) || (baseType.IsIntVector() && argts[i].Main == shaderir.Int)) {
cs.addError(e.Pos(), fmt.Sprintf("invalid arguments %s and %s for %s", baseType.String(), argts[i].String(), callee.BuiltinFunc))
return nil, nil, nil, false
}
}
} else {
if !baseType.Equal(&argts[i]) {
cs.addError(e.Pos(), fmt.Sprintf("all argument types must be the same but got %s and %s for variadic %s", baseType.String(), argts[i].String(), callee.BuiltinFunc))
return nil, nil, nil, false
}
}
args[1].Const = v
argts[1] = shaderir.Type{Main: shaderir.Float}
}
}
finalType = argts[0]
case shaderir.Atan2, shaderir.Pow, shaderir.Mod, shaderir.Step, shaderir.Distance, shaderir.Dot, shaderir.Cross, shaderir.Reflect:
// 2 arguments
if len(args) != 2 {
cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 2 but %d", callee.BuiltinFunc, len(args)))
return nil, nil, nil, false
}
switch callee.BuiltinFunc {
default:
for i := range args {
if args[i].Const != nil && argts[i].Main == shaderir.None {
@@ -650,17 +726,9 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
}
for i := range args {
switch callee.BuiltinFunc {
case shaderir.Min, shaderir.Max:
if argts[i].Main != shaderir.Float && !argts[i].IsFloatVector() && argts[i].Main != shaderir.Int && !argts[i].IsIntVector() {
cs.addError(e.Pos(), fmt.Sprintf("cannot use %s as float, vecN, int, or ivecN value in argument to %s", argts[i].String(), callee.BuiltinFunc))
return nil, nil, nil, false
}
default:
if argts[i].Main != shaderir.Float && !argts[i].IsFloatVector() {
cs.addError(e.Pos(), fmt.Sprintf("cannot use %s as float, vec2, vec3, or vec4 value in argument to %s", argts[i].String(), callee.BuiltinFunc))
return nil, nil, nil, false
}
if argts[i].Main != shaderir.Float && !argts[i].IsFloatVector() {
cs.addError(e.Pos(), fmt.Sprintf("cannot use %s as float, vec2, vec3, or vec4 value in argument to %s", argts[i].String(), callee.BuiltinFunc))
return nil, nil, nil, false
}
}
@@ -670,11 +738,6 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
cs.addError(e.Pos(), fmt.Sprintf("the second argument for %s must equal to the first argument %s or float but %s", callee.BuiltinFunc, argts[0].String(), argts[1].String()))
return nil, nil, nil, false
}
case shaderir.Min, shaderir.Max:
if !(argts[0].Equal(&argts[1]) || (argts[0].IsFloatVector() && argts[1].Main == shaderir.Float) || (argts[0].IsIntVector() && argts[1].Main == shaderir.Int)) {
cs.addError(e.Pos(), fmt.Sprintf("the second argument for %s must equal to the first argument %s or float or int but %s", callee.BuiltinFunc, argts[0].String(), argts[1].String()))
return nil, nil, nil, false
}
case shaderir.Step:
if !argts[0].Equal(&argts[1]) && argts[0].Main != shaderir.Float {
cs.addError(e.Pos(), fmt.Sprintf("the first argument for %s must equal to the second argument %s or float but %s", callee.BuiltinFunc, argts[1].String(), argts[0].String()))
@@ -1034,8 +1097,14 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
cs.addError(e.Pos(), fmt.Sprintf("invalid composite literal type %s", t.String()))
return nil, nil, nil, false
}
if t.Main == shaderir.Array && t.Length == -1 {
t.Length = len(e.Elts)
if t.Main == shaderir.Array {
if t.Length == -1 {
t.Length = len(e.Elts)
} else if t.Length < len(e.Elts) {
// KeyValueExpr is not supported yet. Just compare the length.
cs.addError(e.Pos(), fmt.Sprintf("too many values in %s literal", t.String()))
return nil, nil, nil, false
}
}
idx := block.totalLocalVariableCount()
@@ -1146,6 +1215,31 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
x := exprs[0]
t := ts[0]
// Check the length only when the index is a constant.
if idx.Const != nil {
var length int
switch {
case t.Main == shaderir.Array:
length = t.Length
case t.IsFloatVector() || t.IsIntVector():
length = t.VectorElementCount()
case t.IsMatrix():
length = t.MatrixSize()
default:
cs.addError(e.Pos(), fmt.Sprintf("index operator cannot be applied to the type %s", t.String()))
return nil, nil, nil, false
}
v, ok := gconstant.Int64Val(gconstant.ToInt(idx.Const))
if !ok {
cs.addError(e.Pos(), fmt.Sprintf("constant %s cannot be used as an index", idx.Const.String()))
return nil, nil, nil, false
}
if v < 0 || int(v) >= length {
cs.addError(e.Pos(), fmt.Sprintf("index out of range: %d", v))
return nil, nil, nil, false
}
}
var typ shaderir.Type
switch t.Main {
case shaderir.Vec2, shaderir.Vec3, shaderir.Vec4:
+79 -76
View File
@@ -59,8 +59,6 @@ type compileState struct {
global block
varyingParsed bool
errs []string
}
@@ -202,6 +200,7 @@ func Compile(src []byte, vertexEntry, fragmentEntry string, textureCount int) (*
fragmentEntry: fragmentEntry,
unit: unit,
}
s.ir.SourceHash = shaderir.CalcSourceHash(src)
s.global.ir = &shaderir.Block{}
s.parse(f)
@@ -291,18 +290,17 @@ func (cs *compileState) parse(f *ast.File) {
// Parse function names so that any other function call the others.
// The function data is provisional and will be updated soon.
var vertexInParams []variable
var vertexOutParams []variable
var fragmentInParams []variable
var fragmentOutParams []variable
var fragmentReturnType shaderir.Type
for _, d := range f.Decls {
fd, ok := d.(*ast.FuncDecl)
if !ok {
continue
}
n := fd.Name.Name
if n == cs.vertexEntry {
continue
}
if n == cs.fragmentEntry {
continue
}
for _, f := range cs.funcs {
if f.name == n {
@@ -312,6 +310,19 @@ func (cs *compileState) parse(f *ast.File) {
}
inParams, outParams, ret := cs.parseFuncParams(&cs.global, n, fd)
if n == cs.vertexEntry {
vertexInParams = inParams
vertexOutParams = outParams
continue
}
if n == cs.fragmentEntry {
fragmentInParams = inParams
fragmentOutParams = outParams
fragmentReturnType = ret
continue
}
var inT, outT []shaderir.Type
for _, v := range inParams {
inT = append(inT, v.typ)
@@ -319,7 +330,6 @@ func (cs *compileState) parse(f *ast.File) {
for _, v := range outParams {
outT = append(outT, v.typ)
}
cs.funcs = append(cs.funcs, function{
name: n,
ir: shaderir.Func{
@@ -332,6 +342,45 @@ func (cs *compileState) parse(f *ast.File) {
})
}
// Check varying variables.
// In testings, there might not be vertex and fragment entry points.
if len(vertexOutParams) > 0 && len(fragmentInParams) > 0 {
for i, p := range vertexOutParams {
if len(fragmentInParams) <= i {
break
}
t := fragmentInParams[i].typ
if !p.typ.Equal(&t) {
name := fragmentInParams[i].name
cs.addError(0, fmt.Sprintf("fragment argument %s must be %s but was %s", name, p.typ.String(), t.String()))
}
}
// The first out-param is treated as gl_Position in GLSL.
if vertexOutParams[0].typ.Main != shaderir.Vec4 {
cs.addError(0, "vertex entry point must have at least one returning vec4 value for a position")
}
if len(fragmentOutParams) != 0 || fragmentReturnType.Main != shaderir.Vec4 {
cs.addError(0, "fragment entry point must have one returning vec4 value for a color")
}
}
if len(cs.errs) > 0 {
return
}
// Set attribute and varying veraibles.
for _, p := range vertexInParams {
cs.ir.Attributes = append(cs.ir.Attributes, p.typ)
}
if len(vertexOutParams) > 0 {
// TODO: Check that these params are not arrays or structs
// The 0th argument is a special variable for position and is not included in varying variables.
for _, p := range vertexOutParams[1:] {
cs.ir.Varyings = append(cs.ir.Varyings, p.typ)
}
}
// Parse functions.
for _, d := range f.Decls {
if f, ok := d.(*ast.FuncDecl); ok {
@@ -742,7 +791,13 @@ func (cs *compileState) parseFuncParams(block *block, fname string, d *ast.FuncD
// If there is only one returning value, it is treated as a returning value.
// An array cannot be a returning value, especially for HLSL (#2923).
if len(out) == 1 && out[0].name == "" && out[0].typ.Main != shaderir.Array {
//
// For the vertex entry, a parameter (variable) is used as a returning value.
// For example, GLSL doesn't treat gl_Position as a returning value.
// Thus, the returning value is not set for the vertex entry.
// TODO: This can be resolved by having an indirect function like what the fragment entry already does.
// See internal/shaderir/glsl.adjustProgram.
if len(out) == 1 && out[0].name == "" && out[0].typ.Main != shaderir.Array && fname != cs.vertexEntry {
ret = out[0].typ
out = nil
}
@@ -765,77 +820,25 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool
}
inParams, outParams, returnType := cs.parseFuncParams(block, d.Name.Name, d)
checkVaryings := func(vs []variable) {
if len(cs.ir.Varyings) != len(vs) {
cs.addError(d.Pos(), "the number of vertex entry point's returning values and the number of fragment entry point's params must be the same")
return
if d.Name.Name == cs.fragmentEntry {
if len(inParams) == 0 {
inParams = append(inParams, variable{
name: "_",
typ: shaderir.Type{Main: shaderir.Vec4},
})
}
for i, t := range cs.ir.Varyings {
if t.Main != vs[i].typ.Main {
cs.addError(d.Pos(), "vertex entry point's returning value types and fragment entry point's param types must match")
}
}
}
if block == &cs.global {
switch d.Name.Name {
case cs.vertexEntry:
for _, v := range inParams {
cs.ir.Attributes = append(cs.ir.Attributes, v.typ)
}
// For the vertex entry, a parameter (variable) is used as a returning value.
// For example, GLSL doesn't treat gl_Position as a returning value.
// TODO: This can be resolved by having an indirect function like what the fragment entry already does.
// See internal/shaderir/glsl.adjustProgram.
if len(outParams) == 0 {
outParams = append(outParams, variable{
typ: shaderir.Type{Main: shaderir.Vec4},
// The 0th inParams is a special variable for position and is not included in varying variables.
if diff := len(cs.ir.Varyings) - (len(inParams) - 1); diff > 0 {
// inParams is not enough when the vertex shader has more returning values than the fragment shader's arguments.
orig := len(inParams) - 1
for i := 0; i < diff; i++ {
inParams = append(inParams, variable{
name: "_",
typ: cs.ir.Varyings[orig+i],
})
}
// The first out-param is treated as gl_Position in GLSL.
if outParams[0].typ.Main != shaderir.Vec4 {
cs.addError(d.Pos(), "vertex entry point must have at least one returning vec4 value for a position")
return function{}, false
}
if cs.varyingParsed {
checkVaryings(outParams[1:])
} else {
for _, v := range outParams[1:] {
// TODO: Check that these params are not arrays or structs
cs.ir.Varyings = append(cs.ir.Varyings, v.typ)
}
}
cs.varyingParsed = true
case cs.fragmentEntry:
if len(inParams) == 0 {
cs.addError(d.Pos(), "fragment entry point must have at least one vec4 parameter for a position")
return function{}, false
}
if inParams[0].typ.Main != shaderir.Vec4 {
cs.addError(d.Pos(), "fragment entry point must have at least one vec4 parameter for a position")
return function{}, false
}
if len(outParams) != 0 || returnType.Main != shaderir.Vec4 {
cs.addError(d.Pos(), "fragment entry point must have one returning vec4 value for a color")
return function{}, false
}
if cs.varyingParsed {
checkVaryings(inParams[1:])
} else {
for _, v := range inParams[1:] {
cs.ir.Varyings = append(cs.ir.Varyings, v.typ)
}
}
cs.varyingParsed = true
}
}
b, ok := cs.parseBlock(block, d.Name.Name, d.Body.List, inParams, outParams, returnType, true)
if !ok {
return function{}, false
+23 -12
View File
@@ -117,11 +117,12 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
if op == shaderir.And || op == shaderir.Or || op == shaderir.Xor || op == shaderir.LeftShift || op == shaderir.RightShift {
if lts[0].Main != shaderir.Int && !lts[0].IsIntVector() {
cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: operator %s not defined on %s", stmt.Tok, lts[0].String()))
return nil, false
}
if rts[0].Main != shaderir.Int && !rts[0].IsIntVector() {
cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: operator %s not defined on %s", stmt.Tok, rts[0].String()))
return nil, false
}
return nil, false
}
if lts[0].Main == shaderir.Int && rhs[0].Const != nil {
if !cs.forceToInt(stmt, &rhs[0]) {
@@ -251,7 +252,7 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
if !ok {
return nil, false
}
if len(ts) != 1 || ts[0].Main != shaderir.Bool {
if len(ts) != 1 {
var tss []string
for _, t := range ts {
tss = append(tss, t.String())
@@ -259,6 +260,10 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
cs.addError(stmt.Pos(), fmt.Sprintf("if-condition must be bool but: %s", strings.Join(tss, ", ")))
return nil, false
}
if !(ts[0].Main == shaderir.Bool || (ts[0].Main == shaderir.None && exprs[0].Const != nil && exprs[0].Const.Kind() == gconstant.Bool)) {
cs.addError(stmt.Pos(), fmt.Sprintf("if-condition must be bool but: %s", ts[0].String()))
return nil, false
}
stmts = append(stmts, ss...)
var bs []*shaderir.Block
@@ -493,6 +498,8 @@ func (cs *compileState) assign(block *block, fname string, pos token.Pos, lhs, r
allblank := true
if len(lhs) == len(rhs) {
var localVariablIndicesToAssignLater []int
var leftExprsToAssignLater []shaderir.Expr
for i, e := range lhs {
// Prase RHS first for the order of the statements.
r, rts, ss, ok := cs.parseExpr(block, fname, rhs[i], true)
@@ -615,19 +622,23 @@ func (cs *compileState) assign(block *block, fname string, pos token.Pos, lhs, r
},
r[0],
},
},
shaderir.Stmt{
Type: shaderir.Assign,
Exprs: []shaderir.Expr{
l[0],
{
Type: shaderir.LocalVariable,
Index: idx,
},
},
})
localVariablIndicesToAssignLater = append(localVariablIndicesToAssignLater, idx)
leftExprsToAssignLater = append(leftExprsToAssignLater, l[0])
}
}
for i, idx := range localVariablIndicesToAssignLater {
stmts = append(stmts, shaderir.Stmt{
Type: shaderir.Assign,
Exprs: []shaderir.Expr{
leftExprsToAssignLater[i],
{
Type: shaderir.LocalVariable,
Index: idx,
},
},
})
}
} else {
var ss []shaderir.Stmt
var ok bool
+4
View File
@@ -81,6 +81,10 @@ func (cs *compileState) parseType(block *block, fname string, expr ast.Expr) (sh
cs.addError(t.Pos(), "length of array must be an integer")
return shaderir.Type{}, false
}
if l < 0 {
cs.addError(t.Pos(), fmt.Sprintf("invalid length array %d", l))
return shaderir.Type{}, false
}
length = int(l)
}