mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 11:18: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/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||||
@ -82,6 +83,12 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh
|
|||||||
structNames: map[string]string{},
|
structNames: map[string]string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
indexToFunc := map[int]*shaderir.Func{}
|
||||||
|
for _, f := range p.Funcs {
|
||||||
|
f := f
|
||||||
|
indexToFunc[f.Index] = &f
|
||||||
|
}
|
||||||
|
|
||||||
// Vertex func
|
// Vertex func
|
||||||
var vslines []string
|
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))))
|
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, "")
|
var funcs []*shaderir.Func
|
||||||
for _, f := range p.Funcs {
|
if p.VertexFunc.Block != nil {
|
||||||
vslines = append(vslines, c.glslFunc(p, &f, true)...)
|
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 {
|
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] != "" {
|
if len(vslines) > 0 && vslines[len(vslines)-1] != "" {
|
||||||
vslines = append(vslines, "")
|
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;")
|
fslines = append(fslines, "out vec4 fragColor;")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(p.Funcs) > 0 {
|
var funcs []*shaderir.Func
|
||||||
fslines = append(fslines, "")
|
if p.VertexFunc.Block != nil {
|
||||||
for _, f := range p.Funcs {
|
indices := p.ReferredFuncIndicesInFragmentShader()
|
||||||
fslines = append(fslines, c.glslFunc(p, &f, true)...)
|
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 {
|
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] != "" {
|
if len(fslines) > 0 && fslines[len(fslines)-1] != "" {
|
||||||
fslines = append(fslines, "")
|
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
|
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