restoreble: Add test with shaders rendering an image

Updates #1168
This commit is contained in:
Hajime Hoshi 2020-05-24 17:14:37 +09:00
parent 85730b433e
commit 8738d182fb
3 changed files with 191 additions and 15 deletions

View File

@ -15,7 +15,6 @@
package restorable_test package restorable_test
import ( import (
"image"
"image/color" "image/color"
"testing" "testing"
@ -31,16 +30,15 @@ func TestShader(t *testing.T) {
t.Skip("shader is not available on this environment") t.Skip("shader is not available on this environment")
} }
img := newImageFromImage(image.NewRGBA(image.Rect(0, 0, 1, 1))) img := NewImage(1, 1, false)
defer img.Dispose() defer img.Dispose()
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) s := NewShader(&ir)
is := graphics.QuadIndices()
us := map[int]interface{}{ us := map[int]interface{}{
0: []float32{1, 1}, 0: []float32{1, 1},
} }
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us) img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us)
if err := ResolveStaleImages(); err != nil { if err := ResolveStaleImages(); err != nil {
t.Fatal(err) t.Fatal(err)
@ -55,3 +53,44 @@ func TestShader(t *testing.T) {
t.Errorf("got %v, want %v", got, want) t.Errorf("got %v, want %v", got, want)
} }
} }
func TestShaderChain(t *testing.T) {
if !graphicscommand.IsShaderAvailable() {
t.Skip("shader is not available on this environment")
}
const num = 10
imgs := []*Image{}
for i := 0; i < num; i++ {
img := NewImage(1, 1, false)
defer img.Dispose()
imgs = append(imgs, img)
}
imgs[0].ReplacePixels([]byte{0xff, 0, 0, 0xff}, 0, 0, 1, 1)
ir := etesting.ShaderProgramImages(1)
s := NewShader(&ir)
for i := 0; i < num-1; i++ {
us := map[int]interface{}{
0: []float32{1, 1},
1: imgs[i],
}
imgs[i+1].DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us)
}
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
if err := RestoreIfNeeded(); err != nil {
t.Fatal(err)
}
for i, img := range imgs {
want := color.RGBA{0xff, 0, 0, 0xff}
got := pixelsToColor(img.BasePixelsForTesting(), 0, 0)
if !sameColors(got, want, 1) {
t.Errorf("%d: got %v, want %v", i, got, want)
}
}
}

View File

@ -45,7 +45,7 @@ type VertexFunc struct {
Block Block Block Block
} }
// FragmentFunc takes pseudo in-params, and the number is len(varyings) + 2. // FragmentFunc takes pseudo params, and the number is len(varyings) + 2.
// If index == len(varyings), the param represents the coordinate of the fragment (gl_FragCoord in GLSL). // If index == len(varyings), the param represents the coordinate of the fragment (gl_FragCoord in GLSL).
// If index == len(varyings)+1, the param is an out-param representing the color of the pixel (gl_FragColor in GLSL). // If index == len(varyings)+1, the param is an out-param representing the color of the pixel (gl_FragColor in GLSL).
type FragmentFunc struct { type FragmentFunc struct {

View File

@ -159,7 +159,20 @@ var (
Exprs: []shaderir.Expr{ Exprs: []shaderir.Expr{
{ {
Type: shaderir.LocalVariable, Type: shaderir.LocalVariable,
Index: 4, Index: 4, // the varying variable
},
{
Type: shaderir.LocalVariable,
Index: 1, // the 2nd attribute variable
},
},
},
{
Type: shaderir.Assign,
Exprs: []shaderir.Expr{
{
Type: shaderir.LocalVariable,
Index: 5, // gl_Position in GLSL
}, },
{ {
Type: shaderir.Binary, Type: shaderir.Binary,
@ -174,25 +187,31 @@ var (
}, },
}, },
} }
defaultProgram = shaderir.Program{ )
func defaultProgram() shaderir.Program {
return shaderir.Program{
Uniforms: []shaderir.Type{ Uniforms: []shaderir.Type{
{Main: shaderir.Vec2}, {Main: shaderir.Vec2},
}, },
Attributes: []shaderir.Type{ Attributes: []shaderir.Type{
{Main: shaderir.Vec2}, {Main: shaderir.Vec2}, // Local var (0) in the vertex shader
{Main: shaderir.Vec2}, {Main: shaderir.Vec2}, // Local var (1) in the vertex shader
{Main: shaderir.Vec4}, {Main: shaderir.Vec4}, // Local var (2) in the vertex shader
{Main: shaderir.Vec4}, {Main: shaderir.Vec4}, // Local var (3) in the vertex shader
},
Varyings: []shaderir.Type{
{Main: shaderir.Vec2}, // Local var (4) in the vertex shader, (0) in the fragment shader
}, },
VertexFunc: defaultVertexFunc, VertexFunc: defaultVertexFunc,
} }
) }
// ShaderProgramFill returns a shader intermediate representation to fill the frambuffer. // ShaderProgramFill returns a shader intermediate representation to fill the frambuffer.
// //
// Uniform variables: // Uniform variables:
// //
// 0. the framebuffer size (vec2) // 0: the framebuffer size (Vec2)
func ShaderProgramFill(r, g, b, a byte) shaderir.Program { func ShaderProgramFill(r, g, b, a byte) shaderir.Program {
clr := shaderir.Expr{ clr := shaderir.Expr{
Type: shaderir.Call, Type: shaderir.Call,
@ -220,7 +239,7 @@ func ShaderProgramFill(r, g, b, a byte) shaderir.Program {
}, },
} }
p := defaultProgram p := defaultProgram()
p.FragmentFunc = shaderir.FragmentFunc{ p.FragmentFunc = shaderir.FragmentFunc{
Block: shaderir.Block{ Block: shaderir.Block{
Stmts: []shaderir.Stmt{ Stmts: []shaderir.Stmt{
@ -229,7 +248,7 @@ func ShaderProgramFill(r, g, b, a byte) shaderir.Program {
Exprs: []shaderir.Expr{ Exprs: []shaderir.Expr{
{ {
Type: shaderir.LocalVariable, Type: shaderir.LocalVariable,
Index: 1, Index: 2,
}, },
clr, clr,
}, },
@ -240,3 +259,121 @@ func ShaderProgramFill(r, g, b, a byte) shaderir.Program {
return p return p
} }
// ShaderProgramImages returns a shader intermediate representation to render the frambuffer with the given images.
//
// Uniform variables:
//
// 0: the framebuffer size (Vec2)
// 1: the first images (Sampler2D)
// 3n-1: the (n+1)th image (Sampler2D)
// 3n: the (n+1)th image's size (Vec2)
// 3n+1: the (n+1)th image's region (Vec4)
//
// The first image's size and region are represented in attribute variables.
//
// The size and region values are actually not used in this shader so far.
func ShaderProgramImages(imageNum int) shaderir.Program {
if imageNum <= 0 {
panic("testing: imageNum must be >= 1")
}
p := defaultProgram()
for i := 0; i < imageNum; i++ {
p.Uniforms = append(p.Uniforms, shaderir.Type{Main: shaderir.Sampler2D})
if i > 0 {
p.Uniforms = append(p.Uniforms, shaderir.Type{Main: shaderir.Vec2})
p.Uniforms = append(p.Uniforms, shaderir.Type{Main: shaderir.Vec4})
}
}
// In the fragment shader, local variables are:
//
// 0: Varying variables (vec2)
// 1: gl_FragCoord
// 2: gl_FragColor
// 3: Actual local variables in the main function
local := shaderir.Expr{
Type: shaderir.LocalVariable,
Index: 3,
}
fragColor := shaderir.Expr{
Type: shaderir.LocalVariable,
Index: 2,
}
texPos := shaderir.Expr{
Type: shaderir.LocalVariable,
Index: 0,
}
var stmts []shaderir.Stmt
for i := 0; i < imageNum; i++ {
var rhs shaderir.Expr
if i == 0 {
rhs = shaderir.Expr{
Type: shaderir.Call,
Exprs: []shaderir.Expr{
{
Type: shaderir.BuiltinFuncExpr,
BuiltinFunc: shaderir.Texture2D,
},
{
Type: shaderir.UniformVariable,
Index: 1,
},
texPos,
},
}
} else {
rhs = shaderir.Expr{
Type: shaderir.Binary,
Op: shaderir.Add,
Exprs: []shaderir.Expr{
local,
{
Type: shaderir.Call,
Exprs: []shaderir.Expr{
{
Type: shaderir.BuiltinFuncExpr,
BuiltinFunc: shaderir.Texture2D,
},
{
Type: shaderir.UniformVariable,
Index: 3*i - 1,
},
texPos,
},
},
},
}
}
stmts = append(stmts, shaderir.Stmt{
Type: shaderir.Assign,
Exprs: []shaderir.Expr{
local,
rhs,
},
})
}
stmts = append(stmts, shaderir.Stmt{
Type: shaderir.Assign,
Exprs: []shaderir.Expr{
fragColor,
local,
},
})
p.FragmentFunc = shaderir.FragmentFunc{
Block: shaderir.Block{
LocalVars: []shaderir.Type{
{Main: shaderir.Vec4},
},
Stmts: stmts,
},
}
return p
}