mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 10:48:53 +01:00
ebiten: bug fix: add an indirect function call for a fragment shader
Closes #2245 Closes #2247
This commit is contained in:
parent
652cd169b2
commit
cf92158e33
29
internal/shader/testdata/for5.expected.fs
vendored
29
internal/shader/testdata/for5.expected.fs
vendored
@ -3,27 +3,34 @@ uniform float U1;
|
||||
uniform float U2;
|
||||
|
||||
int F0(in int l0);
|
||||
void F1(in vec4 l0, out vec4 l1);
|
||||
|
||||
int F0(in int l0) {
|
||||
return l0;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
int l0 = 0;
|
||||
void F1(in vec4 l0, out vec4 l1) {
|
||||
int l2 = 0;
|
||||
l0 = 0;
|
||||
for (int l1 = 0; l1 < 10; l1++) {
|
||||
int l2 = 0;
|
||||
l2 = F0(l1);
|
||||
l0 = (l0) + (l2);
|
||||
int l4 = 0;
|
||||
l2 = 0;
|
||||
for (int l3 = 0; l3 < 10; l3++) {
|
||||
int l4 = 0;
|
||||
l4 = F0(l3);
|
||||
l0 = (l0) + (l4);
|
||||
l2 = (l2) + (l4);
|
||||
for (int l5 = 0; l5 < 10; l5++) {
|
||||
int l6 = 0;
|
||||
l6 = F0(l5);
|
||||
l2 = (l2) + (l6);
|
||||
}
|
||||
}
|
||||
l2 = 0;
|
||||
l0 = (l0) + (l2);
|
||||
gl_FragColor = vec4(l0);
|
||||
l4 = 0;
|
||||
l2 = (l2) + (l4);
|
||||
l1 = vec4(l2);
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec4 l0 = vec4(0);
|
||||
F1(gl_FragCoord, l0);
|
||||
gl_FragColor = l0;
|
||||
}
|
||||
|
14
internal/shader/testdata/issue1238.expected.fs
vendored
14
internal/shader/testdata/issue1238.expected.fs
vendored
@ -1,8 +1,16 @@
|
||||
void main(void) {
|
||||
void F0(in vec4 l0, out vec4 l1);
|
||||
|
||||
void F0(in vec4 l0, out vec4 l1) {
|
||||
if (true) {
|
||||
gl_FragColor = gl_FragCoord;
|
||||
l1 = l0;
|
||||
return;
|
||||
}
|
||||
gl_FragColor = gl_FragCoord;
|
||||
l1 = l0;
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec4 l0 = vec4(0);
|
||||
F0(gl_FragCoord, l0);
|
||||
gl_FragColor = l0;
|
||||
}
|
||||
|
18
internal/shader/testdata/issue1245.expected.fs
vendored
18
internal/shader/testdata/issue1245.expected.fs
vendored
@ -1,8 +1,16 @@
|
||||
void main(void) {
|
||||
vec4 l0 = vec4(0);
|
||||
for (float l1 = 0.0; l1 < 4.0; l1++) {
|
||||
(l0).x = ((l0).x) + ((l1) * (1.0000000000e-02));
|
||||
void F0(in vec4 l0, out vec4 l1);
|
||||
|
||||
void F0(in vec4 l0, out vec4 l1) {
|
||||
vec4 l2 = vec4(0);
|
||||
for (float l3 = 0.0; l3 < 4.0; l3++) {
|
||||
(l2).x = ((l2).x) + ((l3) * (1.0000000000e-02));
|
||||
}
|
||||
gl_FragColor = l0;
|
||||
l1 = l2;
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec4 l0 = vec4(0);
|
||||
F0(gl_FragCoord, l0);
|
||||
gl_FragColor = l0;
|
||||
}
|
||||
|
11
internal/shader/testdata/issue1701.expected.fs
vendored
11
internal/shader/testdata/issue1701.expected.fs
vendored
@ -1,5 +1,6 @@
|
||||
void F2(void);
|
||||
void F3(void);
|
||||
void F5(in vec4 l0, out vec4 l1);
|
||||
|
||||
void F2(void) {
|
||||
}
|
||||
@ -8,8 +9,14 @@ void F3(void) {
|
||||
F2();
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
void F5(in vec4 l0, out vec4 l1) {
|
||||
F3();
|
||||
gl_FragColor = vec4(0.0);
|
||||
l1 = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec4 l0 = vec4(0);
|
||||
F5(gl_FragCoord, l0);
|
||||
gl_FragColor = l0;
|
||||
}
|
||||
|
@ -2,7 +2,15 @@ uniform vec2 U0;
|
||||
varying vec2 V0;
|
||||
varying vec4 V1;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4((gl_FragCoord).x, (V0).y, (V1).z, 1.0);
|
||||
void F0(in vec4 l0, in vec2 l1, in vec4 l2, out vec4 l3);
|
||||
|
||||
void F0(in vec4 l0, in vec2 l1, in vec4 l2, out vec4 l3) {
|
||||
l3 = vec4((l0).x, (l1).y, (l2).z, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec4 l0 = vec4(0);
|
||||
F0(gl_FragCoord, V0, V1, l0);
|
||||
gl_FragColor = l0;
|
||||
}
|
||||
|
@ -92,6 +92,8 @@ func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) strin
|
||||
}
|
||||
|
||||
func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentShader string) {
|
||||
p = adjustProgram(p)
|
||||
|
||||
c := &compileContext{
|
||||
version: version,
|
||||
structNames: map[string]string{},
|
||||
@ -625,3 +627,109 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func adjustProgram(p *shaderir.Program) *shaderir.Program {
|
||||
if p.FragmentFunc.Block == nil {
|
||||
return p
|
||||
}
|
||||
|
||||
// Shallow-clone the program in order not to modify p itself.
|
||||
newP := *p
|
||||
|
||||
// Create a new slice not to affect the original p.
|
||||
newP.Funcs = make([]shaderir.Func, len(p.Funcs))
|
||||
copy(newP.Funcs, p.Funcs)
|
||||
|
||||
// Create a new function whose body is the same is the fragment shader's entry point.
|
||||
// The entry point will call this.
|
||||
// This indirect call is needed for these issues:
|
||||
// - Assignment to gl_FragColor doesn't work (#2245)
|
||||
// - There are some odd compilers that don't work with early returns and gl_FragColor (#2247)
|
||||
|
||||
// Determine a unique index of the new function.
|
||||
var funcIdx int
|
||||
for _, f := range newP.Funcs {
|
||||
if funcIdx <= f.Index {
|
||||
funcIdx = f.Index + 1
|
||||
}
|
||||
}
|
||||
|
||||
// For parameters of a fragment func, see the comment in internal/shaderir/program.go.
|
||||
inParams := make([]shaderir.Type, 1+len(newP.Varyings))
|
||||
inParams[0] = shaderir.Type{
|
||||
Main: shaderir.Vec4, // gl_FragCoord
|
||||
}
|
||||
copy(inParams[1:], newP.Varyings)
|
||||
|
||||
outParams := make([]shaderir.Type, 1)
|
||||
outParams[0] = shaderir.Type{
|
||||
Main: shaderir.Vec4, // gl_FragColor
|
||||
}
|
||||
|
||||
newP.Funcs = append(newP.Funcs, shaderir.Func{
|
||||
Index: funcIdx,
|
||||
InParams: inParams,
|
||||
OutParams: outParams,
|
||||
Return: shaderir.Type{},
|
||||
Block: newP.FragmentFunc.Block,
|
||||
})
|
||||
|
||||
// Create an AST to call the new function.
|
||||
call := []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.FunctionExpr,
|
||||
Index: funcIdx,
|
||||
},
|
||||
}
|
||||
for i := 0; i < 1+len(newP.Varyings); i++ {
|
||||
call = append(call, shaderir.Expr{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: i,
|
||||
})
|
||||
}
|
||||
call = append(call, shaderir.Expr{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: 1 + len(newP.Varyings) + 1, // l0 as an out parameter.
|
||||
})
|
||||
|
||||
// Replace the entry point with just calling the new function.
|
||||
stmts := []shaderir.Stmt{
|
||||
{
|
||||
Type: shaderir.ExprStmt,
|
||||
Exprs: []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.Call,
|
||||
Exprs: call,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: shaderir.Assign,
|
||||
Exprs: []shaderir.Expr{
|
||||
// gl_FragColor
|
||||
{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: 1 + len(newP.Varyings),
|
||||
},
|
||||
// l0
|
||||
{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: 1 + len(newP.Varyings) + 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
newP.FragmentFunc = shaderir.FragmentFunc{
|
||||
Block: &shaderir.Block{
|
||||
LocalVars: []shaderir.Type{
|
||||
{
|
||||
Main: shaderir.Vec4,
|
||||
},
|
||||
},
|
||||
LocalVarIndexOffset: 1 + len(newP.Varyings) + 1,
|
||||
Stmts: stmts,
|
||||
},
|
||||
}
|
||||
|
||||
return &newP
|
||||
}
|
||||
|
@ -1019,12 +1019,20 @@ uniform float U0;
|
||||
varying float V0;
|
||||
varying vec2 V1;
|
||||
|
||||
void F0(in vec4 l0, in float l1, in vec2 l2, out vec4 l3);
|
||||
|
||||
void F0(in vec4 l0, in float l1, in vec2 l2, out vec4 l3) {
|
||||
float l4 = float(0);
|
||||
vec2 l5 = vec2(0);
|
||||
l3 = l0;
|
||||
l4 = l1;
|
||||
l5 = l2;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
float l0 = float(0);
|
||||
vec2 l1 = vec2(0);
|
||||
gl_FragColor = gl_FragCoord;
|
||||
l0 = V0;
|
||||
l1 = V1;
|
||||
vec4 l0 = vec4(0);
|
||||
F0(gl_FragCoord, V0, V1, l0);
|
||||
gl_FragColor = l0;
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
@ -1181,3 +1181,58 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #2245, #2247
|
||||
func TestShaderDrawRect(t *testing.T) {
|
||||
const (
|
||||
dstW = 16
|
||||
dstH = 16
|
||||
srcW = 8
|
||||
srcH = 8
|
||||
)
|
||||
|
||||
dst := ebiten.NewImage(dstW, dstH)
|
||||
src := ebiten.NewImage(srcW, srcH)
|
||||
|
||||
s, err := ebiten.NewShader([]byte(`package main
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
// Adjust texCoord into [0, 1].
|
||||
origin, size := imageSrcRegionOnTexture()
|
||||
texCoord -= origin
|
||||
texCoord /= size
|
||||
if texCoord.x >= 0.5 && texCoord.y >= 0.5 {
|
||||
return vec4(1, 0, 0, 1)
|
||||
}
|
||||
return vec4(0, 1, 0, 1)
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const (
|
||||
offsetX = (dstW - srcW) / 2
|
||||
offsetY = (dstH - srcH) / 2
|
||||
)
|
||||
op := &ebiten.DrawRectShaderOptions{}
|
||||
op.GeoM.Translate(offsetX, offsetY)
|
||||
op.Images[0] = src
|
||||
dst.DrawRectShader(srcW, srcH, s, op)
|
||||
for j := 0; j < dstH; j++ {
|
||||
for i := 0; i < dstW; i++ {
|
||||
got := dst.At(i, j).(color.RGBA)
|
||||
var want color.RGBA
|
||||
if offsetX <= i && i < offsetX+srcW && offsetY <= j && j < offsetY+srcH {
|
||||
if offsetX+srcW/2 <= i && offsetY+srcH/2 <= j {
|
||||
want = color.RGBA{0xff, 0, 0, 0xff}
|
||||
} else {
|
||||
want = color.RGBA{0, 0xff, 0, 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