shader: Use arrays at uniform variables

Fixes #1274
This commit is contained in:
Hajime Hoshi 2020-08-01 16:20:49 +09:00
parent 2b4cf7fd17
commit a4334c5464
8 changed files with 87 additions and 59 deletions

View File

@ -23,19 +23,14 @@ const (
// PreservedUniformVariablesNum represents the number of preserved uniform variables. // PreservedUniformVariablesNum represents the number of preserved uniform variables.
// Any shaders in Ebiten must have these uniform variables. // Any shaders in Ebiten must have these uniform variables.
//
// All the preversed uniform variables are vec2 so far.
PreservedUniformVariablesNum = 1 + // the destination texture size PreservedUniformVariablesNum = 1 + // the destination texture size
ShaderImageNum + // the texture sizes 1 + // the texture sizes array
(ShaderImageNum - 1) // the offsets of the second and the following images 1 // the offsets array of the second and the following images
)
func TextureOffsetUniformVariableIndex(i int) int { DestinationTextureSizeUniformVariableIndex = 0
if i == 0 { TextureSizesUniformVariableIndex = 1
panic("graphics: the texture 0 doesn't have its offset") TextureOffsetsUniformVariableIndex = 2
} )
return 1 + ShaderImageNum + i - 1
}
const ( const (
IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles. IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles.

View File

@ -311,24 +311,32 @@ func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]
us[0].value = []float32{float32(vw), float32(vh)} us[0].value = []float32{float32(vw), float32(vh)}
us[0].typ = s.ir.Uniforms[0] us[0].typ = s.ir.Uniforms[0]
vsizes := make([]float32, 2*len(srcs))
for i, src := range srcs { for i, src := range srcs {
img := g.images[src] if img := g.images[src]; img != nil {
var w, h int w, h := img.framebufferSize()
if img != nil { vsizes[2*i] = float32(w)
w, h = img.framebufferSize() vsizes[2*i+1] = float32(h)
} }
const offset = 1
us[i+offset].name = fmt.Sprintf("U%d", i+offset) }
us[i+offset].value = []float32{float32(w), float32(h)} {
us[i+offset].typ = s.ir.Uniforms[i+offset] const idx = 1
us[idx].name = fmt.Sprintf("U%d", idx)
us[idx].value = vsizes
us[idx].typ = s.ir.Uniforms[idx]
} }
voffsets := make([]float32, 2*len(offsets))
for i, o := range offsets { for i, o := range offsets {
const offset = 1 + graphics.ShaderImageNum voffsets[2*i] = o[0]
o := o voffsets[2*i+1] = o[1]
us[i+offset].name = fmt.Sprintf("U%d", i+offset) }
us[i+offset].value = o[:] {
us[i+offset].typ = s.ir.Uniforms[i+offset] const idx = 1 + 1
us[idx].name = fmt.Sprintf("U%d", idx)
us[idx].value = voffsets
us[idx].typ = s.ir.Uniforms[idx]
} }
for i, v := range uniforms { for i, v := range uniforms {

View File

@ -182,11 +182,6 @@ func (cs *compileState) parse(f *ast.File) {
} }
} }
// TODO: Check len(unames) == graphics.PreservedUniformVariablesNum. Unfortunately this is not true on tests. // TODO: Check len(unames) == graphics.PreservedUniformVariablesNum. Unfortunately this is not true on tests.
for _, t := range utypes {
if got, want := t.Main, shaderir.Vec2; got != want {
panic(fmt.Sprintf("shader: all the preserved uniform variables' types must be %v but %v", want, got))
}
}
for i, u := range cs.uniforms { for i, u := range cs.uniforms {
if !strings.HasPrefix(u, "__") { if !strings.HasPrefix(u, "__") {
unames = append(unames, u) unames = append(unames, u)

View File

@ -1,17 +1,17 @@
uniform vec2[4] U0; uniform vec2 U0[4];
void F0(out vec2[2] l0); void F0(out vec2 l0[2]);
void F1(out vec2[2] l0); void F1(out vec2 l0[2]);
void F0(out vec2[2] l0) { void F0(out vec2 l0[2]) {
vec2[2] l1 = vec2[2](vec2(0), vec2(0)); vec2 l1[2] = vec2[2](vec2(0), vec2(0));
l0 = l1; l0 = l1;
return; return;
} }
void F1(out vec2[2] l0) { void F1(out vec2 l0[2]) {
vec2[2] l1 = vec2[2](vec2(0), vec2(0)); vec2 l1[2] = vec2[2](vec2(0), vec2(0));
vec2[2] l2 = vec2[2](vec2(0), vec2(0)); vec2 l2[2] = vec2[2](vec2(0), vec2(0));
(l1)[0] = vec2(1.0); (l1)[0] = vec2(1.0);
l2 = l1; l2 = l1;
((l2)[1]).y = vec2(2.0); ((l2)[1]).y = vec2(2.0);

View File

@ -168,12 +168,12 @@ func (p *Program) Glsl() (vertexShader, fragmentShader string) {
return strings.Join(vslines, "\n") + "\n", strings.Join(fslines, "\n") + "\n" return strings.Join(vslines, "\n") + "\n", strings.Join(fslines, "\n") + "\n"
} }
func (p *Program) glslType(t *Type) string { func (p *Program) glslType(t *Type) (string, string) {
switch t.Main { switch t.Main {
case None: case None:
return "void" return "void", ""
case Struct: case Struct:
return p.structName(t) return p.structName(t), ""
default: default:
return t.Glsl() return t.Glsl()
} }
@ -186,7 +186,8 @@ func (p *Program) glslVarDecl(t *Type, varname string) string {
case Struct: case Struct:
return fmt.Sprintf("%s %s", p.structName(t), varname) return fmt.Sprintf("%s %s", p.structName(t), varname)
default: default:
return fmt.Sprintf("%s %s", t.Glsl(), varname) t0, t1 := t.Glsl()
return fmt.Sprintf("%s %s%s", t0, varname, t1)
} }
} }
@ -200,7 +201,8 @@ func (p *Program) glslVarInit(t *Type) string {
for i := 0; i < t.Length; i++ { for i := 0; i < t.Length; i++ {
es = append(es, init) es = append(es, init)
} }
return fmt.Sprintf("%s[%d](%s)", t.Sub[0].Glsl(), t.Length, strings.Join(es, ", ")) t0, t1 := t.Glsl()
return fmt.Sprintf("%s%s(%s)", t0, t1, strings.Join(es, ", "))
case Struct: case Struct:
panic("not implemented") panic("not implemented")
case Bool: case Bool:
@ -222,7 +224,8 @@ func (p *Program) glslVarInit(t *Type) string {
case Mat4: case Mat4:
return "mat4(0)" return "mat4(0)"
default: default:
panic(fmt.Sprintf("?(unexpected type: %s)", p.glslType(t))) t0, t1 := p.glslType(t)
panic(fmt.Sprintf("?(unexpected type: %s%s)", t0, t1))
} }
} }
@ -242,7 +245,8 @@ func (p *Program) glslFunc(f *Func, prototype bool) []string {
argsstr = strings.Join(args, ", ") argsstr = strings.Join(args, ", ")
} }
sig := fmt.Sprintf("%s F%d(%s)", p.glslType(&f.Return), f.Index, argsstr) t0, t1 := p.glslType(&f.Return)
sig := fmt.Sprintf("%s%s F%d(%s)", t0, t1, f.Index, argsstr)
var lines []string var lines []string
if prototype { if prototype {
@ -436,7 +440,8 @@ func (p *Program) glslBlock(topBlock, block *Block, level int, localVarIndex int
t := s.ForVarType t := s.ForVarType
init := constantToNumberLiteral(ct, s.ForInit) init := constantToNumberLiteral(ct, s.ForInit)
end := constantToNumberLiteral(ct, s.ForEnd) end := constantToNumberLiteral(ct, s.ForEnd)
lines = append(lines, fmt.Sprintf("%sfor (%s %s = %s; %s %s %s; %s) {", idt, t.Glsl(), v, init, v, op, end, delta)) t0, t1 := t.Glsl()
lines = append(lines, fmt.Sprintf("%sfor (%s %s%s = %s; %s %s %s; %s) {", idt, t0, v, t1, init, v, op, end, delta))
lines = append(lines, p.glslBlock(topBlock, &s.Blocks[0], level+1, localVarIndex)...) lines = append(lines, p.glslBlock(topBlock, &s.Blocks[0], level+1, localVarIndex)...)
lines = append(lines, fmt.Sprintf("%s}", idt)) lines = append(lines, fmt.Sprintf("%s}", idt))
case Continue: case Continue:

View File

@ -85,14 +85,15 @@ func (t *Type) serialize() string {
return t.String() return t.String()
} }
func (t *Type) Glsl() string { func (t *Type) Glsl() (string, string) {
switch t.Main { switch t.Main {
case Array: case Array:
return fmt.Sprintf("%s[%d]", t.Sub[0].Glsl(), t.Length) t0, t1 := t.Sub[0].Glsl()
return t0 + t1, fmt.Sprintf("[%d]", t.Length)
case Struct: case Struct:
panic("shaderir: a struct is not implemented") panic("shaderir: a struct is not implemented")
default: default:
return t.Main.glsl() return t.Main.glsl(), ""
} }
} }

View File

@ -225,8 +225,19 @@ func defaultProgram() shaderir.Program {
} }
p.Uniforms = make([]shaderir.Type, graphics.PreservedUniformVariablesNum) p.Uniforms = make([]shaderir.Type, graphics.PreservedUniformVariablesNum)
for i := range p.Uniforms { // Destination texture size
p.Uniforms[i] = shaderir.Type{Main: shaderir.Vec2} p.Uniforms[0] = shaderir.Type{Main: shaderir.Vec2}
// Source texture sizes
p.Uniforms[1] = shaderir.Type{
Main: shaderir.Array,
Length: graphics.ShaderImageNum,
Sub: []shaderir.Type{{Main: shaderir.Vec2}},
}
// Source texture offsets
p.Uniforms[2] = shaderir.Type{
Main: shaderir.Array,
Length: graphics.ShaderImageNum - 1,
Sub: []shaderir.Type{{Main: shaderir.Vec2}},
} }
return p return p
} }
@ -350,8 +361,18 @@ func ShaderProgramImages(imageNum int) shaderir.Program {
Exprs: []shaderir.Expr{ Exprs: []shaderir.Expr{
texPos, texPos,
{ {
Type: shaderir.UniformVariable, Type: shaderir.Index,
Index: graphics.TextureOffsetUniformVariableIndex(i), Exprs: []shaderir.Expr{
{
Type: shaderir.UniformVariable,
Index: graphics.TextureOffsetsUniformVariableIndex,
},
{
Type: shaderir.NumberExpr,
Const: constant.MakeInt64(int64(i - 1)),
ConstType: shaderir.ConstTypeInt,
},
},
}, },
}, },
} }

View File

@ -36,23 +36,26 @@ func textureDstSize() vec2 {
} }
` `
shaderSuffix += fmt.Sprintf(`
var __textureSizes [%d]vec2
`, graphics.ShaderImageNum)
for i := 0; i < graphics.ShaderImageNum; i++ { for i := 0; i < graphics.ShaderImageNum; i++ {
shaderSuffix += fmt.Sprintf(` shaderSuffix += fmt.Sprintf(`
var __textureSize%[1]d vec2
func texture%[1]dSize() vec2 { func texture%[1]dSize() vec2 {
return __textureSize%[1]d return __textureSizes[%[1]d]
} }
`, i) `, i)
} }
shaderSuffix += fmt.Sprintf(`
var __textureOffsets [%d]vec2
`, graphics.ShaderImageNum-1)
for i := 0; i < graphics.ShaderImageNum; i++ { for i := 0; i < graphics.ShaderImageNum; i++ {
var offset string var offset string
if i >= 1 { if i >= 1 {
shaderSuffix += fmt.Sprintf(` offset = fmt.Sprintf(" + __textureOffsets[%d]", i-1)
var __textureOffset%[1]d vec2
`, i)
offset = fmt.Sprintf(" + __textureOffset%d", i)
} }
// __t%d is a special variable for a texture variable. // __t%d is a special variable for a texture variable.
shaderSuffix += fmt.Sprintf(` shaderSuffix += fmt.Sprintf(`