internal/shader: bug fix: % operators for integer vecs didn't work

Closes #1911
This commit is contained in:
Hajime Hoshi 2022-11-21 00:49:34 +09:00
parent 0b9cbaa1ed
commit e9e5d27d2c
3 changed files with 108 additions and 18 deletions

View File

@ -228,7 +228,7 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
if lhs[0].Const != nil && rhs[0].Const != nil {
op := e.Op
// https://golang.org/pkg/go/constant/#BinaryOp
// https://pkg.go.dev/go/constant/#BinaryOp
// "To force integer division of Int operands, use op == token.QUO_ASSIGN instead of
// token.QUO; the result is guaranteed to be Int in this case."
if op == token.QUO && lhs[0].Const.Kind() == gconstant.Int && rhs[0].Const.Kind() == gconstant.Int {
@ -255,25 +255,25 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
v = gconstant.MakeBool(gconstant.Compare(lhs[0].Const, op, rhs[0].Const))
}
t = shaderir.Type{Main: shaderir.Bool}
default:
if op == token.REM {
if !isModAvailableForConsts(&lhs[0], &rhs[0]) {
var wrongTypeName string
if lhs[0].Const.Kind() != gconstant.Int {
wrongTypeName = goConstantKindString(lhs[0].Const.Kind())
} else {
wrongTypeName = goConstantKindString(rhs[0].Const.Kind())
}
cs.addError(e.Pos(), fmt.Sprintf("invalid operation: operator %% not defined on untyped %s", wrongTypeName))
return nil, nil, nil, false
}
if !cs.forceToInt(e, &lhs[0]) {
return nil, nil, nil, false
}
if !cs.forceToInt(e, &rhs[0]) {
return nil, nil, nil, false
case token.REM:
if !isModAvailableForConsts(&lhs[0], &rhs[0]) {
var wrongTypeName string
if lhs[0].Const.Kind() != gconstant.Int {
wrongTypeName = goConstantKindString(lhs[0].Const.Kind())
} else {
wrongTypeName = goConstantKindString(rhs[0].Const.Kind())
}
cs.addError(e.Pos(), fmt.Sprintf("invalid operation: operator %% not defined on untyped %s", wrongTypeName))
return nil, nil, nil, false
}
if !cs.forceToInt(e, &lhs[0]) {
return nil, nil, nil, false
}
if !cs.forceToInt(e, &rhs[0]) {
return nil, nil, nil, false
}
fallthrough
default:
v = gconstant.BinaryOp(lhs[0].Const, op, rhs[0].Const)
if v.Kind() == gconstant.Float {
t = shaderir.Type{Main: shaderir.Float}
@ -348,6 +348,9 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
cs.addError(e.Pos(), fmt.Sprintf("types don't match: %s %s %s", lhst.String(), e.Op, rhst.String()))
return nil, nil, nil, false
}
if !cs.forceToInt(e, &rhs[0]) {
return nil, nil, nil, false
}
case shaderir.Int:
if !canTruncateToInteger(rhs[0].Const) {
cs.addError(e.Pos(), fmt.Sprintf("constant %s truncated to integer", rhs[0].Const.String()))

View File

@ -35,6 +35,30 @@ const (
// utilFunctions is GLSL utility functions for old GLSL versions.
const utilFunctions = `int modInt(int x, int y) {
return x - y*(x/y);
}
ivec2 modInt(ivec2 x, int y) {
return x - y*(x/y);
}
ivec3 modInt(ivec3 x, int y) {
return x - y*(x/y);
}
ivec4 modInt(ivec4 x, int y) {
return x - y*(x/y);
}
ivec2 modInt(ivec2 x, ivec2 y) {
return x - y*(x/y);
}
ivec3 modInt(ivec3 x, ivec3 y) {
return x - y*(x/y);
}
ivec4 modInt(ivec4 x, ivec4 y) {
return x - y*(x/y);
}`
func VertexPrelude(version GLSLVersion) string {

View File

@ -15,6 +15,7 @@
package ebiten_test
import (
"fmt"
"image"
"image/color"
"math"
@ -1503,3 +1504,65 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
t.Errorf("got: %v, want: %v", got, want)
}
}
func TestShaderIVecMod(t *testing.T) {
cases := []struct {
source string
want color.RGBA
}{
{
source: `a := ivec4(0x24, 0x3f, 0x6a, 0x88)
return vec4(a)/255`,
want: color.RGBA{0x24, 0x3f, 0x6a, 0x88},
},
{
source: `a := ivec4(0x24, 0x3f, 0x6a, 0x88)
a %= 0x85
return vec4(a)/255`,
want: color.RGBA{0x24, 0x3f, 0x6a, 0x03},
},
{
source: `a := ivec4(0x24, 0x3f, 0x6a, 0x88)
a %= ivec4(0x85, 0xa3, 0x08, 0xd3)
return vec4(a)/255`,
want: color.RGBA{0x24, 0x3f, 0x02, 0x88},
},
{
source: `a := ivec4(0x24, 0x3f, 0x6a, 0x88)
b := a % 0x85
return vec4(b)/255`,
want: color.RGBA{0x24, 0x3f, 0x6a, 0x03},
},
{
source: `a := ivec4(0x24, 0x3f, 0x6a, 0x88)
b := a % ivec4(0x85, 0xa3, 0x08, 0xd3)
return vec4(b)/255`,
want: color.RGBA{0x24, 0x3f, 0x02, 0x88},
},
}
for _, tc := range cases {
shader := fmt.Sprintf(`package main
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
%s
}
`, tc.source)
const w, h = 1, 1
dst := ebiten.NewImage(w, h)
defer dst.Dispose()
s, err := ebiten.NewShader([]byte(shader))
if err != nil {
t.Fatal(err)
}
defer s.Dispose()
op := &ebiten.DrawRectShaderOptions{}
dst.DrawRectShader(w, h, s, op)
if got, want := dst.At(0, 0).(color.RGBA), tc.want; !sameColors(got, want, 1) {
t.Errorf("%s: got: %v, want: %v", tc.source, got, want)
}
}
}