mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
internal/shaderir/glsl: Bug fix: Remove uncalled functions
Some built-in functions like dFdx is not available in a vertex shader, then a function that calls such built-in function should not be in a vertex shader. Closes #1701
This commit is contained in:
parent
b0106e95b9
commit
853c1f2b92
15
internal/shader/testdata/issue1701.expected.fs
vendored
Normal file
15
internal/shader/testdata/issue1701.expected.fs
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
void F2(void);
|
||||
void F3(void);
|
||||
|
||||
void F2(void) {
|
||||
}
|
||||
|
||||
void F3(void) {
|
||||
F2();
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
F3();
|
||||
gl_FragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
29
internal/shader/testdata/issue1701.expected.vs
vendored
Normal file
29
internal/shader/testdata/issue1701.expected.vs
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
attribute vec2 A0;
|
||||
|
||||
void F0(void);
|
||||
void F1(void);
|
||||
void F2(void);
|
||||
void F3(void);
|
||||
|
||||
void F0(void) {
|
||||
F1();
|
||||
F2();
|
||||
}
|
||||
|
||||
void F1(void) {
|
||||
F2();
|
||||
F3();
|
||||
}
|
||||
|
||||
void F2(void) {
|
||||
}
|
||||
|
||||
void F3(void) {
|
||||
F2();
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
F0();
|
||||
gl_Position = vec4(0.0);
|
||||
return;
|
||||
}
|
35
internal/shader/testdata/issue1701.go
vendored
Normal file
35
internal/shader/testdata/issue1701.go
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
func Foo() {
|
||||
Bar()
|
||||
Baz()
|
||||
}
|
||||
|
||||
func Bar() {
|
||||
Baz()
|
||||
Quux()
|
||||
}
|
||||
|
||||
func Baz() {
|
||||
}
|
||||
|
||||
func Quux() {
|
||||
Baz()
|
||||
}
|
||||
|
||||
func NeverCalled() {
|
||||
Foo()
|
||||
Bar()
|
||||
Baz()
|
||||
Quux()
|
||||
}
|
||||
|
||||
func Vertex(pos vec2) vec4 {
|
||||
Foo()
|
||||
return vec4(0)
|
||||
}
|
||||
|
||||
func Fragment(pos vec4) vec4 {
|
||||
Quux()
|
||||
return vec4(0)
|
||||
}
|
@ -19,6 +19,7 @@ import (
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
@ -82,6 +83,12 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh
|
||||
structNames: map[string]string{},
|
||||
}
|
||||
|
||||
indexToFunc := map[int]*shaderir.Func{}
|
||||
for _, f := range p.Funcs {
|
||||
f := f
|
||||
indexToFunc[f.Index] = &f
|
||||
}
|
||||
|
||||
// Vertex func
|
||||
var vslines []string
|
||||
{
|
||||
@ -110,16 +117,33 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh
|
||||
vslines = append(vslines, fmt.Sprintf("%s %s;", keyword, c.glslVarDecl(p, &t, fmt.Sprintf("V%d", i))))
|
||||
}
|
||||
}
|
||||
if len(p.Funcs) > 0 {
|
||||
vslines = append(vslines, "")
|
||||
for _, f := range p.Funcs {
|
||||
vslines = append(vslines, c.glslFunc(p, &f, true)...)
|
||||
|
||||
var funcs []*shaderir.Func
|
||||
if p.VertexFunc.Block != nil {
|
||||
indices := p.ReferredFuncIndicesInVertexShader()
|
||||
sort.Ints(indices)
|
||||
funcs = make([]*shaderir.Func, 0, len(indices))
|
||||
for _, idx := range indices {
|
||||
funcs = append(funcs, indexToFunc[idx])
|
||||
}
|
||||
} else {
|
||||
// When a vertex entry point is not defined, allow to put all the functions. This is useful for testing.
|
||||
funcs = make([]*shaderir.Func, 0, len(p.Funcs))
|
||||
for _, f := range p.Funcs {
|
||||
f := f
|
||||
funcs = append(funcs, &f)
|
||||
}
|
||||
}
|
||||
if len(funcs) > 0 {
|
||||
vslines = append(vslines, "")
|
||||
for _, f := range funcs {
|
||||
vslines = append(vslines, c.glslFunc(p, f, true)...)
|
||||
}
|
||||
for _, f := range funcs {
|
||||
if len(vslines) > 0 && vslines[len(vslines)-1] != "" {
|
||||
vslines = append(vslines, "")
|
||||
}
|
||||
vslines = append(vslines, c.glslFunc(p, &f, false)...)
|
||||
vslines = append(vslines, c.glslFunc(p, f, false)...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,16 +180,32 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh
|
||||
fslines = append(fslines, "out vec4 fragColor;")
|
||||
}
|
||||
|
||||
if len(p.Funcs) > 0 {
|
||||
fslines = append(fslines, "")
|
||||
for _, f := range p.Funcs {
|
||||
fslines = append(fslines, c.glslFunc(p, &f, true)...)
|
||||
var funcs []*shaderir.Func
|
||||
if p.VertexFunc.Block != nil {
|
||||
indices := p.ReferredFuncIndicesInFragmentShader()
|
||||
sort.Ints(indices)
|
||||
funcs = make([]*shaderir.Func, 0, len(indices))
|
||||
for _, idx := range indices {
|
||||
funcs = append(funcs, indexToFunc[idx])
|
||||
}
|
||||
} else {
|
||||
// When a fragment entry point is not defined, allow to put all the functions. This is useful for testing.
|
||||
funcs = make([]*shaderir.Func, 0, len(p.Funcs))
|
||||
for _, f := range p.Funcs {
|
||||
f := f
|
||||
funcs = append(funcs, &f)
|
||||
}
|
||||
}
|
||||
if len(funcs) > 0 {
|
||||
fslines = append(fslines, "")
|
||||
for _, f := range funcs {
|
||||
fslines = append(fslines, c.glslFunc(p, f, true)...)
|
||||
}
|
||||
for _, f := range funcs {
|
||||
if len(fslines) > 0 && fslines[len(fslines)-1] != "" {
|
||||
fslines = append(fslines, "")
|
||||
}
|
||||
fslines = append(fslines, c.glslFunc(p, &f, false)...)
|
||||
fslines = append(fslines, c.glslFunc(p, f, false)...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -347,3 +347,55 @@ func IsValidSwizzling(s string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Program) ReferredFuncIndicesInVertexShader() []int {
|
||||
return p.referredFuncIndicesInBlockEntryPoint(p.VertexFunc.Block)
|
||||
}
|
||||
|
||||
func (p *Program) ReferredFuncIndicesInFragmentShader() []int {
|
||||
return p.referredFuncIndicesInBlockEntryPoint(p.FragmentFunc.Block)
|
||||
}
|
||||
|
||||
func (p *Program) referredFuncIndicesInBlockEntryPoint(b *Block) []int {
|
||||
indexToFunc := map[int]*Func{}
|
||||
for _, f := range p.Funcs {
|
||||
f := f
|
||||
indexToFunc[f.Index] = &f
|
||||
}
|
||||
visited := map[int]struct{}{}
|
||||
return referredFuncIndicesInBlock(b, indexToFunc, visited)
|
||||
}
|
||||
|
||||
func referredFuncIndicesInBlock(b *Block, indexToFunc map[int]*Func, visited map[int]struct{}) []int {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var fs []int
|
||||
|
||||
for _, s := range b.Stmts {
|
||||
for _, e := range s.Exprs {
|
||||
fs = append(fs, referredFuncIndicesInExpr(&e, indexToFunc, visited)...)
|
||||
}
|
||||
for _, bb := range s.Blocks {
|
||||
fs = append(fs, referredFuncIndicesInBlock(bb, indexToFunc, visited)...)
|
||||
}
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
func referredFuncIndicesInExpr(e *Expr, indexToFunc map[int]*Func, visited map[int]struct{}) []int {
|
||||
var fs []int
|
||||
|
||||
if e.Type == FunctionExpr {
|
||||
if _, ok := visited[e.Index]; !ok {
|
||||
fs = append(fs, e.Index)
|
||||
visited[e.Index] = struct{}{}
|
||||
fs = append(fs, referredFuncIndicesInBlock(indexToFunc[e.Index].Block, indexToFunc, visited)...)
|
||||
}
|
||||
}
|
||||
for _, ee := range e.Exprs {
|
||||
fs = append(fs, referredFuncIndicesInExpr(&ee, indexToFunc, visited)...)
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
@ -1053,3 +1053,61 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #1701
|
||||
func TestShaderDerivatives2(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
||||
s, err := NewShader([]byte(`package main
|
||||
|
||||
// This function uses dfdx and then should not be in GLSL's vertex shader (#1701).
|
||||
func Foo(p vec4) vec4 {
|
||||
return vec4(abs(dfdx(p.r)), abs(dfdy(p.g)), 0, 1)
|
||||
}
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
p := imageSrc0At(texCoord)
|
||||
return Foo(p)
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dst := NewImage(w, h)
|
||||
src := 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 {
|
||||
pix[4*(j*w+i)] = 0xff
|
||||
}
|
||||
if j < h/2 {
|
||||
pix[4*(j*w+i)+1] = 0xff
|
||||
}
|
||||
pix[4*(j*w+i)+3] = 0xff
|
||||
}
|
||||
}
|
||||
src.ReplacePixels(pix)
|
||||
|
||||
op := &DrawRectShaderOptions{}
|
||||
op.Images[0] = src
|
||||
dst.DrawRectShader(w, h, s, op)
|
||||
|
||||
// The results of the edges might be unreliable. Skip the edges.
|
||||
for j := 1; j < h-1; j++ {
|
||||
for i := 1; i < w-1; i++ {
|
||||
got := dst.At(i, j).(color.RGBA)
|
||||
want := color.RGBA{0, 0, 0, 0xff}
|
||||
if i == w/2-1 || i == w/2 {
|
||||
want.R = 0xff
|
||||
}
|
||||
if j == h/2-1 || j == h/2 {
|
||||
want.G = 0xff
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user