internal/shader: implement a new built-in function discard

Closes #1969
This commit is contained in:
Hajime Hoshi 2022-08-09 11:31:56 +09:00
parent 2b248ef783
commit 205245b094
4 changed files with 64 additions and 2 deletions

View File

@ -469,6 +469,14 @@ func (cs *compileState) parseExpr(block *block, expr ast.Expr, markLocalVariable
case shaderir.Texture2DF:
// TODO: Check arg types.
t = shaderir.Type{Main: shaderir.Vec4}
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)))
}
stmts = append(stmts, shaderir.Stmt{
Type: shaderir.Discard,
})
return nil, nil, stmts, true
default:
// TODO: Check arg types.
// If the argument is a non-typed constant value, treat is as a float value (#1874).

View File

@ -503,7 +503,7 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
lines = append(lines, fmt.Sprintf("%sreturn %s;", idt, expr(&s.Exprs[0])))
}
case shaderir.Discard:
lines = append(lines, idt+"discard;")
lines = append(lines, idt+"discard_fragment();")
default:
lines = append(lines, fmt.Sprintf("%s?(unexpected stmt: %d)", idt, s.Type))
}

View File

@ -265,6 +265,7 @@ const (
Dfdx BuiltinFunc = "dfdx"
Dfdy BuiltinFunc = "dfdy"
Fwidth BuiltinFunc = "fwidth"
DiscardF BuiltinFunc = "discard"
)
func ParseBuiltinFunc(str string) (BuiltinFunc, bool) {
@ -317,7 +318,8 @@ func ParseBuiltinFunc(str string) (BuiltinFunc, bool) {
Texture2DF,
Dfdx,
Dfdy,
Fwidth:
Fwidth,
DiscardF:
return BuiltinFunc(str), true
}
return "", false

View File

@ -1129,3 +1129,55 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
}
}
}
// Issue #1969
func TestShaderDiscard(t *testing.T) {
const w, h = 16, 16
dst := ebiten.NewImage(w, h)
dst.Fill(color.RGBA{0xff, 0, 0, 0xff})
src := ebiten.NewImage(w, h)
pix := make([]byte, 4*w*h)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
if i >= w/2 || j >= h/2 {
continue
}
pix[4*(j*w+i)+3] = 0xff
}
}
src.WritePixels(pix)
s, err := ebiten.NewShader([]byte(`package main
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
p := imageSrc0At(texCoord)
if p.a == 0 {
discard()
} else {
return vec4(0, 1, 0, 1)
}
}
`))
if err != nil {
t.Fatal(err)
}
op := &ebiten.DrawRectShaderOptions{}
op.Images[0] = src
dst.DrawRectShader(w, h, s, op)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
got := dst.At(i, j).(color.RGBA)
want := color.RGBA{0, 0xff, 0x00, 0xff}
if i >= w/2 || j >= h/2 {
want = color.RGBA{0xff, 0, 0x00, 0xff}
}
if !sameColors(got, want, 2) {
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
}