diff --git a/internal/shader/expr.go b/internal/shader/expr.go index aa3c21cf1..40399cf29 100644 --- a/internal/shader/expr.go +++ b/internal/shader/expr.go @@ -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). diff --git a/internal/shaderir/msl/msl.go b/internal/shaderir/msl/msl.go index 22acd3789..dbffbcaa0 100644 --- a/internal/shaderir/msl/msl.go +++ b/internal/shaderir/msl/msl.go @@ -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)) } diff --git a/internal/shaderir/program.go b/internal/shaderir/program.go index 0a75ccebe..b9443ed2f 100644 --- a/internal/shaderir/program.go +++ b/internal/shaderir/program.go @@ -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 diff --git a/shader_test.go b/shader_test.go index c911312c8..b081fdd39 100644 --- a/shader_test.go +++ b/shader_test.go @@ -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) + } + } + } +}