ebiten/internal/shader/delayed.go

166 lines
4.4 KiB
Go
Raw Normal View History

2024-02-28 12:27:26 +01:00
// Copyright 2024 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 shader
import (
2024-03-02 07:32:31 +01:00
"fmt"
2024-02-28 12:27:26 +01:00
gconstant "go/constant"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
2024-03-02 07:32:31 +01:00
type delayedTypeValidator interface {
Validate(t shaderir.Type) (shaderir.Type, bool)
IsValidated() (shaderir.Type, bool)
Error() string
}
2024-02-28 12:27:26 +01:00
2024-03-02 07:32:31 +01:00
func isArgDefaultTypeInt(f shaderir.BuiltinFunc) bool {
return f == shaderir.IntF || f == shaderir.IVec2F || f == shaderir.IVec3F || f == shaderir.IVec4F
}
2024-02-28 12:27:26 +01:00
2024-03-02 07:32:31 +01:00
func isIntType(t shaderir.Type) bool {
return t.Main == shaderir.Int || t.IsIntVector()
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
func (cs *compileState) ValidateDefaultTypesForExpr(block *block, expr shaderir.Expr, t shaderir.Type) shaderir.Type {
if check, ok := cs.delayedTypeCheks[expr.Ast]; ok {
if resT, ok := check.IsValidated(); ok {
return resT
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
resT, ok := check.Validate(t)
if !ok {
return shaderir.Type{Main: shaderir.None}
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
return resT
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
switch expr.Type {
case shaderir.LocalVariable:
return block.vars[expr.Index].typ
2024-02-28 12:27:26 +01:00
2024-03-02 07:32:31 +01:00
case shaderir.Binary:
left := expr.Exprs[0]
right := expr.Exprs[1]
2024-02-28 12:27:26 +01:00
2024-03-02 07:32:31 +01:00
leftType := cs.ValidateDefaultTypesForExpr(block, left, t)
rightType := cs.ValidateDefaultTypesForExpr(block, right, t)
2024-02-28 12:27:26 +01:00
2024-03-02 07:32:31 +01:00
// Usure about top-level type, try to validate by neighbour type
// The same work is done twice. Can it be optimized?
if t.Main == shaderir.None {
cs.ValidateDefaultTypesForExpr(block, left, rightType)
cs.ValidateDefaultTypesForExpr(block, right, leftType)
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
case shaderir.Call:
fun := expr.Exprs[0]
if fun.Type == shaderir.BuiltinFuncExpr {
if isArgDefaultTypeInt(fun.BuiltinFunc) {
for _, e := range expr.Exprs[1:] {
cs.ValidateDefaultTypesForExpr(block, e, shaderir.Type{Main: shaderir.Int})
}
return shaderir.Type{Main: shaderir.Int}
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
for _, e := range expr.Exprs[1:] {
cs.ValidateDefaultTypesForExpr(block, e, shaderir.Type{Main: shaderir.Float})
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
return shaderir.Type{Main: shaderir.Float}
}
if fun.Type == shaderir.FunctionExpr {
args := cs.funcs[fun.Index].ir.InParams
for i, e := range expr.Exprs[1:] {
cs.ValidateDefaultTypesForExpr(block, e, args[i])
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
retT := cs.funcs[fun.Index].ir.Return
return retT
}
}
return shaderir.Type{Main: shaderir.None}
}
func (cs *compileState) ValidateDefaultTypes(block *block, stmt shaderir.Stmt) {
switch stmt.Type {
case shaderir.Assign:
left := stmt.Exprs[0]
right := stmt.Exprs[1]
if left.Type == shaderir.LocalVariable {
varType := block.vars[left.Index].typ
// Type is not explicitly specified
if stmt.IsTypeGuessed {
varType = shaderir.Type{Main: shaderir.None}
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
cs.ValidateDefaultTypesForExpr(block, right, varType)
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
case shaderir.ExprStmt:
for _, e := range stmt.Exprs {
cs.ValidateDefaultTypesForExpr(block, e, shaderir.Type{Main: shaderir.None})
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
}
}
2024-02-28 12:27:26 +01:00
2024-03-02 07:32:31 +01:00
type delayedShiftValidator struct {
shiftType shaderir.Op
value gconstant.Value
validated bool
closestUnknown bool
failed bool
}
func (d *delayedShiftValidator) IsValidated() (shaderir.Type, bool) {
if d.failed {
return shaderir.Type{}, false
}
if d.validated {
return shaderir.Type{Main: shaderir.Int}, true
}
// If only matched with None
if d.closestUnknown {
// Was it originally represented by an int constant?
if d.value.Kind() == gconstant.Int {
return shaderir.Type{Main: shaderir.Int}, true
}
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
return shaderir.Type{}, false
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
func (d *delayedShiftValidator) Validate(t shaderir.Type) (shaderir.Type, bool) {
if d.validated {
return shaderir.Type{Main: shaderir.Int}, true
}
if isIntType(t) {
d.validated = true
return shaderir.Type{Main: shaderir.Int}, true
}
if t.Main == shaderir.None {
d.closestUnknown = true
return t, true
}
return shaderir.Type{Main: shaderir.None}, false
2024-02-28 12:27:26 +01:00
}
2024-03-02 07:32:31 +01:00
func (d *delayedShiftValidator) Error() string {
st := "left shift"
if d.shiftType == shaderir.RightShift {
st = "right shift"
}
return fmt.Sprintf("left operand for %s should be int", st)
2024-02-28 12:27:26 +01:00
}