ebiten/internal/shaderir/program.go

511 lines
10 KiB
Go
Raw Normal View History

2020-05-11 17:19:42 +02:00
// 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.
2020-05-16 16:42:32 +02:00
// Package shaderir offers intermediate representation for shader programs.
2020-05-11 17:19:42 +02:00
package shaderir
2020-06-20 16:26:29 +02:00
import (
"encoding/hex"
"go/constant"
2020-06-20 16:26:29 +02:00
"go/token"
"hash/fnv"
2022-11-03 09:44:11 +01:00
"sort"
"strings"
2020-06-20 16:26:29 +02:00
)
type Unit int
const (
Texels Unit = iota
Pixels
)
type SourceHash [16]byte
func CalcSourceHash(source []byte) SourceHash {
h := fnv.New128a()
_, _ = h.Write(source)
var hash SourceHash
h.Sum(hash[:0])
return hash
}
func (s SourceHash) String() string {
return hex.EncodeToString(s[:])
}
2020-05-11 17:19:42 +02:00
type Program struct {
2024-04-05 20:39:14 +02:00
UniformNames []string
Uniforms []Type
TextureCount int
ColorsOutCount int
Attributes []Type
Varyings []Type
Funcs []Func
VertexFunc VertexFunc
FragmentFunc FragmentFunc
Unit Unit
SourceHash SourceHash
uniformFactors []uint32
2020-05-11 17:19:42 +02:00
}
type Func struct {
2020-06-07 11:57:46 +02:00
Index int
InParams []Type
OutParams []Type
Return Type
2020-08-09 08:55:59 +02:00
Block *Block
2020-05-11 17:19:42 +02:00
}
2020-05-16 15:18:58 +02:00
// VertexFunc takes pseudo params, and the number if len(attributes) + len(varyings) + 1.
2020-06-02 17:46:52 +02:00
// If 0 <= index < len(attributes), the params are in-params and represent attribute variables.
// If index == len(attributes), the param is an out-param and represents the position in vec4 (gl_Position in GLSL)
2020-06-02 17:46:52 +02:00
// If len(attributes) + 1 <= index < len(attributes) + len(varyings) + 1, the params are out-params and represent
// varying variables.
2020-05-16 15:18:58 +02:00
type VertexFunc struct {
2020-08-09 08:55:59 +02:00
Block *Block
2020-05-16 15:18:58 +02:00
}
// FragmentFunc takes pseudo params, and the number is len(varyings) + 2.
// If index == 0, the param represents the coordinate of the fragment (gl_FragCoord in GLSL).
// If 0 < index <= len(varyings), the param represents (index-1)th varying variable.
2020-05-16 15:18:58 +02:00
type FragmentFunc struct {
2020-08-09 08:55:59 +02:00
Block *Block
2020-05-16 15:18:58 +02:00
}
2020-05-11 17:19:42 +02:00
type Block struct {
LocalVars []Type
LocalVarIndexOffset int
Stmts []Stmt
2020-05-11 17:19:42 +02:00
}
type Stmt struct {
Type StmtType
Exprs []Expr
2020-08-09 08:55:59 +02:00
Blocks []*Block
ForVarType Type
ForVarIndex int
ForInit constant.Value
ForEnd constant.Value
ForOp Op
ForDelta constant.Value
InitIndex int
2020-05-11 17:19:42 +02:00
}
type StmtType int
const (
ExprStmt StmtType = iota
2020-05-13 18:45:33 +02:00
BlockStmt
2020-05-11 17:19:42 +02:00
Assign
Init
2020-05-11 17:19:42 +02:00
If
For
Continue
Break
2020-05-13 20:14:39 +02:00
Return
2020-05-11 17:19:42 +02:00
Discard
)
type Expr struct {
2020-05-16 17:25:31 +02:00
Type ExprType
Exprs []Expr
Const constant.Value
2020-05-16 17:25:31 +02:00
BuiltinFunc BuiltinFunc
2020-05-16 20:00:57 +02:00
Swizzling string
Index int
2020-05-16 17:25:31 +02:00
Op Op
2020-05-11 17:19:42 +02:00
}
type ExprType int
const (
Blank ExprType = iota
NumberExpr
UniformVariable
TextureVariable
LocalVariable
StructMember
2020-05-16 17:25:31 +02:00
BuiltinFuncExpr
2020-05-16 19:24:35 +02:00
SwizzlingExpr
2020-05-16 20:00:57 +02:00
FunctionExpr
2020-05-11 17:19:42 +02:00
Unary
Binary
2020-05-16 10:22:17 +02:00
Selection
2020-05-11 17:19:42 +02:00
Call
2020-05-16 10:22:17 +02:00
FieldSelector
2020-05-11 17:19:42 +02:00
Index
)
type Op int
2020-05-11 17:19:42 +02:00
const (
Add Op = iota
Sub
NotOp
ComponentWiseMul
MatrixMul
Div
ModOp
LeftShift
RightShift
LessThanOp
LessThanEqualOp
GreaterThanOp
GreaterThanEqualOp
EqualOp
NotEqualOp
VectorEqualOp
VectorNotEqualOp
And
Xor
Or
AndAnd
OrOr
2020-05-16 17:25:31 +02:00
)
func OpFromToken(t token.Token, lhs, rhs Type) (Op, bool) {
2020-06-20 16:26:29 +02:00
switch t {
case token.ADD:
return Add, true
case token.SUB:
return Sub, true
case token.NOT:
return NotOp, true
case token.MUL:
if lhs.IsMatrix() || rhs.IsMatrix() {
return MatrixMul, true
}
return ComponentWiseMul, true
2020-06-20 16:26:29 +02:00
case token.QUO:
return Div, true
case token.QUO_ASSIGN:
// QUO_ASSIGN indicates an integer division.
// https://pkg.go.dev/go/constant/#BinaryOp
return Div, true
2020-06-20 16:26:29 +02:00
case token.REM:
return ModOp, true
case token.SHL:
return LeftShift, true
case token.SHR:
return RightShift, true
case token.LSS:
return LessThanOp, true
case token.LEQ:
return LessThanEqualOp, true
case token.GTR:
return GreaterThanOp, true
case token.GEQ:
return GreaterThanEqualOp, true
case token.EQL:
if lhs.IsFloatVector() || lhs.IsIntVector() || rhs.IsFloatVector() || rhs.IsIntVector() {
return VectorEqualOp, true
}
2020-06-20 16:26:29 +02:00
return EqualOp, true
case token.NEQ:
if lhs.IsFloatVector() || lhs.IsIntVector() || rhs.IsFloatVector() || rhs.IsIntVector() {
return VectorNotEqualOp, true
}
2020-06-20 16:26:29 +02:00
return NotEqualOp, true
case token.AND:
return And, true
case token.XOR:
return Xor, true
case token.OR:
return Or, true
case token.LAND:
return AndAnd, true
case token.LOR:
return OrOr, true
}
return 0, false
2020-06-20 16:26:29 +02:00
}
2020-05-16 17:25:31 +02:00
type BuiltinFunc string
const (
Len BuiltinFunc = "len"
Cap BuiltinFunc = "cap"
BoolF BuiltinFunc = "bool"
IntF BuiltinFunc = "int"
FloatF BuiltinFunc = "float"
Vec2F BuiltinFunc = "vec2"
Vec3F BuiltinFunc = "vec3"
Vec4F BuiltinFunc = "vec4"
IVec2F BuiltinFunc = "ivec2"
IVec3F BuiltinFunc = "ivec3"
IVec4F BuiltinFunc = "ivec4"
Mat2F BuiltinFunc = "mat2"
Mat3F BuiltinFunc = "mat3"
Mat4F BuiltinFunc = "mat4"
Radians BuiltinFunc = "radians" // This function is not used yet (#2253)
Degrees BuiltinFunc = "degrees" // This function is not used yet (#2253)
Sin BuiltinFunc = "sin"
Cos BuiltinFunc = "cos"
Tan BuiltinFunc = "tan"
Asin BuiltinFunc = "asin"
Acos BuiltinFunc = "acos"
Atan BuiltinFunc = "atan"
Atan2 BuiltinFunc = "atan2"
Pow BuiltinFunc = "pow"
Exp BuiltinFunc = "exp"
Log BuiltinFunc = "log"
Exp2 BuiltinFunc = "exp2"
Log2 BuiltinFunc = "log2"
Sqrt BuiltinFunc = "sqrt"
Inversesqrt BuiltinFunc = "inversesqrt"
Abs BuiltinFunc = "abs"
Sign BuiltinFunc = "sign"
Floor BuiltinFunc = "floor"
Ceil BuiltinFunc = "ceil"
Fract BuiltinFunc = "fract"
Mod BuiltinFunc = "mod"
Min BuiltinFunc = "min"
Max BuiltinFunc = "max"
Clamp BuiltinFunc = "clamp"
Mix BuiltinFunc = "mix"
Step BuiltinFunc = "step"
Smoothstep BuiltinFunc = "smoothstep"
Length BuiltinFunc = "length"
Distance BuiltinFunc = "distance"
Dot BuiltinFunc = "dot"
Cross BuiltinFunc = "cross"
Normalize BuiltinFunc = "normalize"
Faceforward BuiltinFunc = "faceforward"
Reflect BuiltinFunc = "reflect"
Refract BuiltinFunc = "refract"
Transpose BuiltinFunc = "transpose"
Dfdx BuiltinFunc = "dfdx"
Dfdy BuiltinFunc = "dfdy"
Fwidth BuiltinFunc = "fwidth"
DiscardF BuiltinFunc = "discard"
TexelAt BuiltinFunc = "__texelAt"
2020-05-11 17:19:42 +02:00
)
2020-05-31 11:11:55 +02:00
func ParseBuiltinFunc(str string) (BuiltinFunc, bool) {
switch BuiltinFunc(str) {
case Len,
Cap,
BoolF,
IntF,
FloatF,
Vec2F,
2020-05-31 11:11:55 +02:00
Vec3F,
Vec4F,
IVec2F,
IVec3F,
IVec4F,
2020-05-31 11:11:55 +02:00
Mat2F,
Mat3F,
Mat4F,
Sin,
Cos,
Tan,
Asin,
Acos,
Atan,
Atan2,
2020-05-31 11:11:55 +02:00
Pow,
Exp,
Log,
Exp2,
Log2,
Sqrt,
Inversesqrt,
Abs,
Sign,
Floor,
Ceil,
Fract,
Mod,
Min,
Max,
Clamp,
Mix,
Step,
Smoothstep,
Length,
Distance,
Dot,
Cross,
Normalize,
Faceforward,
Reflect,
Refract,
2020-05-31 11:11:55 +02:00
Transpose,
Dfdx,
Dfdy,
Fwidth,
DiscardF,
TexelAt:
2020-05-31 11:11:55 +02:00
return BuiltinFunc(str), true
}
return "", false
}
func IsValidSwizzling(s string) bool {
if len(s) < 1 || 4 < len(s) {
return false
}
const (
xyzw = "xyzw"
rgba = "rgba"
strq = "strq"
)
switch {
case strings.IndexByte(xyzw, s[0]) >= 0:
for _, c := range s {
if strings.IndexRune(xyzw, c) == -1 {
return false
}
}
return true
case strings.IndexByte(rgba, s[0]) >= 0:
for _, c := range s {
if strings.IndexRune(rgba, c) == -1 {
return false
}
}
return true
case strings.IndexByte(strq, s[0]) >= 0:
for _, c := range s {
if strings.IndexRune(strq, c) == -1 {
return false
}
}
return true
}
return false
}
func (p *Program) ReachableFuncsFromBlock(block *Block) []*Func {
indexToFunc := map[int]*Func{}
for _, f := range p.Funcs {
f := f
indexToFunc[f.Index] = &f
}
2022-11-03 10:16:54 +01:00
visited := map[int]struct{}{}
2022-11-03 10:16:54 +01:00
var indices []int
var f func(expr *Expr)
f = func(expr *Expr) {
if expr.Type != FunctionExpr {
return
}
if _, ok := visited[expr.Index]; ok {
return
}
indices = append(indices, expr.Index)
visited[expr.Index] = struct{}{}
walkExprs(f, indexToFunc[expr.Index].Block)
}
walkExprs(f, block)
2022-11-03 09:44:11 +01:00
sort.Ints(indices)
2022-11-03 10:16:54 +01:00
2022-11-03 09:44:11 +01:00
funcs := make([]*Func, 0, len(indices))
for _, i := range indices {
funcs = append(funcs, indexToFunc[i])
}
return funcs
}
2022-11-03 10:16:54 +01:00
func walkExprs(f func(expr *Expr), block *Block) {
if block == nil {
return
}
2022-11-03 10:16:54 +01:00
for _, s := range block.Stmts {
for _, e := range s.Exprs {
2022-11-03 10:16:54 +01:00
walkExprsInExpr(f, &e)
}
2022-11-03 10:16:54 +01:00
for _, b := range s.Blocks {
walkExprs(f, b)
}
}
}
2022-11-03 10:16:54 +01:00
func walkExprsInExpr(f func(expr *Expr), expr *Expr) {
if expr == nil {
return
}
2022-11-03 10:16:54 +01:00
f(expr)
for _, e := range expr.Exprs {
walkExprsInExpr(f, &e)
}
}
func (p *Program) appendReachableUniformVariablesFromBlock(indices []int, block *Block) []int {
indexToFunc := map[int]*Func{}
for _, f := range p.Funcs {
f := f
indexToFunc[f.Index] = &f
}
visitedFuncs := map[int]struct{}{}
indicesSet := map[int]struct{}{}
var f func(expr *Expr)
f = func(expr *Expr) {
switch expr.Type {
case UniformVariable:
2023-05-05 07:24:48 +02:00
if _, ok := indicesSet[expr.Index]; ok {
return
}
indicesSet[expr.Index] = struct{}{}
2023-05-05 07:24:48 +02:00
indices = append(indices, expr.Index)
case FunctionExpr:
if _, ok := visitedFuncs[expr.Index]; ok {
return
}
visitedFuncs[expr.Index] = struct{}{}
walkExprs(f, indexToFunc[expr.Index].Block)
}
}
walkExprs(f, block)
return indices
}
2023-12-24 15:10:41 +01:00
// FilterUniformVariables replaces uniform variables with 0 when they are not used.
2022-12-03 07:37:14 +01:00
// By minimizing uniform variables, more commands can be merged in the graphicscommand package.
func (p *Program) FilterUniformVariables(uniforms []uint32) {
if p.uniformFactors == nil {
indices := p.appendReachableUniformVariablesFromBlock(nil, p.VertexFunc.Block)
indices = p.appendReachableUniformVariablesFromBlock(indices, p.FragmentFunc.Block)
2023-05-04 07:04:48 +02:00
reachableUniforms := make([]bool, len(p.Uniforms))
for _, idx := range indices {
reachableUniforms[idx] = true
}
p.uniformFactors = make([]uint32, len(uniforms))
var idx int
2022-12-03 15:09:41 +01:00
for i, typ := range p.Uniforms {
c := typ.Uint32Count()
2023-05-04 07:04:48 +02:00
if reachableUniforms[i] {
for i := idx; i < idx+c; i++ {
p.uniformFactors[i] = 1
2023-05-04 07:04:48 +02:00
}
}
idx += c
2022-12-03 15:09:41 +01:00
}
}
for i, factor := range p.uniformFactors {
uniforms[i] *= factor
}
}