glsl.go (15096B)
1 // Copyright 2020 The Ebiten Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package glsl 16 17 import ( 18 "fmt" 19 "go/constant" 20 "go/token" 21 "regexp" 22 "strings" 23 24 "github.com/hajimehoshi/ebiten/v2/internal/shaderir" 25 ) 26 27 const FragmentPrelude = `#if defined(GL_ES) 28 precision highp float; 29 #else 30 #define lowp 31 #define mediump 32 #define highp 33 #endif` 34 35 type compileContext struct { 36 structNames map[string]string 37 structTypes []shaderir.Type 38 } 39 40 func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) string { 41 if t.Main != shaderir.Struct { 42 panic("glsl: the given type at structName must be a struct") 43 } 44 s := t.String() 45 if n, ok := c.structNames[s]; ok { 46 return n 47 } 48 n := fmt.Sprintf("S%d", len(c.structNames)) 49 c.structNames[s] = n 50 c.structTypes = append(c.structTypes, *t) 51 return n 52 } 53 54 func Compile(p *shaderir.Program) (vertexShader, fragmentShader string) { 55 c := &compileContext{ 56 structNames: map[string]string{}, 57 } 58 59 // Vertex func 60 var vslines []string 61 { 62 vslines = append(vslines, "{{.Structs}}") 63 if len(p.Uniforms) > 0 || p.TextureNum > 0 || len(p.Attributes) > 0 || len(p.Varyings) > 0 { 64 vslines = append(vslines, "") 65 for i, t := range p.Uniforms { 66 vslines = append(vslines, fmt.Sprintf("uniform %s;", c.glslVarDecl(p, &t, fmt.Sprintf("U%d", i)))) 67 } 68 for i := 0; i < p.TextureNum; i++ { 69 vslines = append(vslines, fmt.Sprintf("uniform sampler2D T%d;", i)) 70 } 71 for i, t := range p.Attributes { 72 vslines = append(vslines, fmt.Sprintf("attribute %s;", c.glslVarDecl(p, &t, fmt.Sprintf("A%d", i)))) 73 } 74 for i, t := range p.Varyings { 75 vslines = append(vslines, fmt.Sprintf("varying %s;", c.glslVarDecl(p, &t, fmt.Sprintf("V%d", i)))) 76 } 77 } 78 if len(p.Funcs) > 0 { 79 vslines = append(vslines, "") 80 for _, f := range p.Funcs { 81 vslines = append(vslines, c.glslFunc(p, &f, true)...) 82 } 83 for _, f := range p.Funcs { 84 if len(vslines) > 0 && vslines[len(vslines)-1] != "" { 85 vslines = append(vslines, "") 86 } 87 vslines = append(vslines, c.glslFunc(p, &f, false)...) 88 } 89 } 90 91 if p.VertexFunc.Block != nil && len(p.VertexFunc.Block.Stmts) > 0 { 92 vslines = append(vslines, "") 93 vslines = append(vslines, "void main(void) {") 94 vslines = append(vslines, c.glslBlock(p, p.VertexFunc.Block, p.VertexFunc.Block, 0)...) 95 vslines = append(vslines, "}") 96 } 97 } 98 99 // Fragment func 100 var fslines []string 101 { 102 fslines = append(fslines, strings.Split(FragmentPrelude, "\n")...) 103 fslines = append(fslines, "", "{{.Structs}}") 104 if len(p.Uniforms) > 0 || p.TextureNum > 0 || len(p.Varyings) > 0 { 105 fslines = append(fslines, "") 106 for i, t := range p.Uniforms { 107 fslines = append(fslines, fmt.Sprintf("uniform %s;", c.glslVarDecl(p, &t, fmt.Sprintf("U%d", i)))) 108 } 109 for i := 0; i < p.TextureNum; i++ { 110 fslines = append(fslines, fmt.Sprintf("uniform sampler2D T%d;", i)) 111 } 112 for i, t := range p.Varyings { 113 fslines = append(fslines, fmt.Sprintf("varying %s;", c.glslVarDecl(p, &t, fmt.Sprintf("V%d", i)))) 114 } 115 } 116 if len(p.Funcs) > 0 { 117 fslines = append(fslines, "") 118 for _, f := range p.Funcs { 119 fslines = append(fslines, c.glslFunc(p, &f, true)...) 120 } 121 for _, f := range p.Funcs { 122 if len(fslines) > 0 && fslines[len(fslines)-1] != "" { 123 fslines = append(fslines, "") 124 } 125 fslines = append(fslines, c.glslFunc(p, &f, false)...) 126 } 127 } 128 129 if p.FragmentFunc.Block != nil && len(p.FragmentFunc.Block.Stmts) > 0 { 130 fslines = append(fslines, "") 131 fslines = append(fslines, "void main(void) {") 132 fslines = append(fslines, c.glslBlock(p, p.FragmentFunc.Block, p.FragmentFunc.Block, 0)...) 133 fslines = append(fslines, "}") 134 } 135 } 136 137 vs := strings.Join(vslines, "\n") 138 fs := strings.Join(fslines, "\n") 139 140 // Struct types are determined after converting the program. 141 if len(c.structTypes) > 0 { 142 var stlines []string 143 for i, t := range c.structTypes { 144 stlines = append(stlines, fmt.Sprintf("struct S%d {", i)) 145 for j, st := range t.Sub { 146 stlines = append(stlines, fmt.Sprintf("\t%s;", c.glslVarDecl(p, &st, fmt.Sprintf("M%d", j)))) 147 } 148 stlines = append(stlines, "};") 149 } 150 st := strings.Join(stlines, "\n") 151 vs = strings.ReplaceAll(vs, "{{.Structs}}", st) 152 fs = strings.ReplaceAll(fs, "{{.Structs}}", st) 153 } else { 154 vs = strings.ReplaceAll(vs, "{{.Structs}}", "") 155 fs = strings.ReplaceAll(fs, "{{.Structs}}", "") 156 } 157 158 nls := regexp.MustCompile(`\n\n+`) 159 vs = nls.ReplaceAllString(vs, "\n\n") 160 fs = nls.ReplaceAllString(fs, "\n\n") 161 162 vs = strings.TrimSpace(vs) + "\n" 163 fs = strings.TrimSpace(fs) + "\n" 164 165 return vs, fs 166 } 167 168 func (c *compileContext) glslType(p *shaderir.Program, t *shaderir.Type) (string, string) { 169 switch t.Main { 170 case shaderir.None: 171 return "void", "" 172 case shaderir.Struct: 173 return c.structName(p, t), "" 174 default: 175 return typeString(t) 176 } 177 } 178 179 func (c *compileContext) glslVarDecl(p *shaderir.Program, t *shaderir.Type, varname string) string { 180 switch t.Main { 181 case shaderir.None: 182 return "?(none)" 183 case shaderir.Struct: 184 return fmt.Sprintf("%s %s", c.structName(p, t), varname) 185 default: 186 t0, t1 := typeString(t) 187 return fmt.Sprintf("%s %s%s", t0, varname, t1) 188 } 189 } 190 191 func (c *compileContext) glslVarInit(p *shaderir.Program, t *shaderir.Type) string { 192 switch t.Main { 193 case shaderir.None: 194 return "?(none)" 195 case shaderir.Array: 196 init := c.glslVarInit(p, &t.Sub[0]) 197 es := make([]string, 0, t.Length) 198 for i := 0; i < t.Length; i++ { 199 es = append(es, init) 200 } 201 t0, t1 := typeString(t) 202 return fmt.Sprintf("%s%s(%s)", t0, t1, strings.Join(es, ", ")) 203 case shaderir.Struct: 204 panic("not implemented") 205 case shaderir.Bool: 206 return "false" 207 case shaderir.Int: 208 return "0" 209 case shaderir.Float, shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4: 210 return fmt.Sprintf("%s(0)", basicTypeString(t.Main)) 211 default: 212 t0, t1 := c.glslType(p, t) 213 panic(fmt.Sprintf("?(unexpected type: %s%s)", t0, t1)) 214 } 215 } 216 217 func (c *compileContext) glslFunc(p *shaderir.Program, f *shaderir.Func, prototype bool) []string { 218 var args []string 219 var idx int 220 for _, t := range f.InParams { 221 args = append(args, "in "+c.glslVarDecl(p, &t, fmt.Sprintf("l%d", idx))) 222 idx++ 223 } 224 for _, t := range f.OutParams { 225 args = append(args, "out "+c.glslVarDecl(p, &t, fmt.Sprintf("l%d", idx))) 226 idx++ 227 } 228 argsstr := "void" 229 if len(args) > 0 { 230 argsstr = strings.Join(args, ", ") 231 } 232 233 t0, t1 := c.glslType(p, &f.Return) 234 sig := fmt.Sprintf("%s%s F%d(%s)", t0, t1, f.Index, argsstr) 235 236 var lines []string 237 if prototype { 238 lines = append(lines, fmt.Sprintf("%s;", sig)) 239 return lines 240 } 241 lines = append(lines, fmt.Sprintf("%s {", sig)) 242 lines = append(lines, c.glslBlock(p, f.Block, f.Block, 0)...) 243 lines = append(lines, "}") 244 245 return lines 246 } 247 248 func constantToNumberLiteral(t shaderir.ConstType, v constant.Value) string { 249 switch t { 250 case shaderir.ConstTypeNone: 251 if v.Kind() == constant.Bool { 252 if constant.BoolVal(v) { 253 return "true" 254 } 255 return "false" 256 } 257 fallthrough 258 case shaderir.ConstTypeFloat: 259 if i := constant.ToInt(v); i.Kind() == constant.Int { 260 x, _ := constant.Int64Val(i) 261 return fmt.Sprintf("%d.0", x) 262 } 263 if i := constant.ToFloat(v); i.Kind() == constant.Float { 264 x, _ := constant.Float64Val(i) 265 return fmt.Sprintf("%.10e", x) 266 } 267 case shaderir.ConstTypeInt: 268 if i := constant.ToInt(v); i.Kind() == constant.Int { 269 x, _ := constant.Int64Val(i) 270 return fmt.Sprintf("%d", x) 271 } 272 } 273 return fmt.Sprintf("?(unexpected literal: %s)", v) 274 } 275 276 func localVariableName(p *shaderir.Program, topBlock, block *shaderir.Block, idx int) string { 277 switch topBlock { 278 case p.VertexFunc.Block: 279 na := len(p.Attributes) 280 nv := len(p.Varyings) 281 switch { 282 case idx < na: 283 return fmt.Sprintf("A%d", idx) 284 case idx == na: 285 return "gl_Position" 286 case idx < na+nv+1: 287 return fmt.Sprintf("V%d", idx-na-1) 288 default: 289 return fmt.Sprintf("l%d", idx-(na+nv+1)) 290 } 291 case p.FragmentFunc.Block: 292 nv := len(p.Varyings) 293 switch { 294 case idx == 0: 295 return "gl_FragCoord" 296 case idx < nv+1: 297 return fmt.Sprintf("V%d", idx-1) 298 case idx == nv+1: 299 return "gl_FragColor" 300 default: 301 return fmt.Sprintf("l%d", idx-(nv+2)) 302 } 303 default: 304 return fmt.Sprintf("l%d", idx) 305 } 306 } 307 308 func (c *compileContext) initVariable(p *shaderir.Program, topBlock, block *shaderir.Block, index int, decl bool, level int) []string { 309 idt := strings.Repeat("\t", level+1) 310 name := localVariableName(p, topBlock, block, index) 311 t := p.LocalVariableType(topBlock, block, index) 312 313 var lines []string 314 switch t.Main { 315 case shaderir.Array: 316 if decl { 317 lines = append(lines, fmt.Sprintf("%s%s;", idt, c.glslVarDecl(p, &t, name))) 318 } 319 init := c.glslVarInit(p, &t.Sub[0]) 320 for i := 0; i < t.Length; i++ { 321 lines = append(lines, fmt.Sprintf("%s%s[%d] = %s;", idt, name, i, init)) 322 } 323 case shaderir.None: 324 // The type is None e.g., when the variable is a for-loop counter. 325 default: 326 if decl { 327 lines = append(lines, fmt.Sprintf("%s%s = %s;", idt, c.glslVarDecl(p, &t, name), c.glslVarInit(p, &t))) 328 } else { 329 lines = append(lines, fmt.Sprintf("%s%s = %s;", idt, name, c.glslVarInit(p, &t))) 330 } 331 } 332 return lines 333 } 334 335 func (c *compileContext) glslBlock(p *shaderir.Program, topBlock, block *shaderir.Block, level int) []string { 336 if block == nil { 337 return nil 338 } 339 340 var lines []string 341 for i := range block.LocalVars { 342 lines = append(lines, c.initVariable(p, topBlock, block, block.LocalVarIndexOffset+i, true, level)...) 343 } 344 345 var glslExpr func(e *shaderir.Expr) string 346 glslExpr = func(e *shaderir.Expr) string { 347 switch e.Type { 348 case shaderir.NumberExpr: 349 return constantToNumberLiteral(e.ConstType, e.Const) 350 case shaderir.UniformVariable: 351 return fmt.Sprintf("U%d", e.Index) 352 case shaderir.TextureVariable: 353 return fmt.Sprintf("T%d", e.Index) 354 case shaderir.LocalVariable: 355 return localVariableName(p, topBlock, block, e.Index) 356 case shaderir.StructMember: 357 return fmt.Sprintf("M%d", e.Index) 358 case shaderir.BuiltinFuncExpr: 359 return builtinFuncString(e.BuiltinFunc) 360 case shaderir.SwizzlingExpr: 361 if !shaderir.IsValidSwizzling(e.Swizzling) { 362 return fmt.Sprintf("?(unexpected swizzling: %s)", e.Swizzling) 363 } 364 return e.Swizzling 365 case shaderir.FunctionExpr: 366 return fmt.Sprintf("F%d", e.Index) 367 case shaderir.Unary: 368 var op string 369 switch e.Op { 370 case shaderir.Add, shaderir.Sub, shaderir.NotOp: 371 op = string(e.Op) 372 default: 373 op = fmt.Sprintf("?(unexpected op: %s)", string(e.Op)) 374 } 375 return fmt.Sprintf("%s(%s)", op, glslExpr(&e.Exprs[0])) 376 case shaderir.Binary: 377 return fmt.Sprintf("(%s) %s (%s)", glslExpr(&e.Exprs[0]), e.Op, glslExpr(&e.Exprs[1])) 378 case shaderir.Selection: 379 return fmt.Sprintf("(%s) ? (%s) : (%s)", glslExpr(&e.Exprs[0]), glslExpr(&e.Exprs[1]), glslExpr(&e.Exprs[2])) 380 case shaderir.Call: 381 var args []string 382 for _, exp := range e.Exprs[1:] { 383 args = append(args, glslExpr(&exp)) 384 } 385 // Using parentheses at the callee is illegal. 386 return fmt.Sprintf("%s(%s)", glslExpr(&e.Exprs[0]), strings.Join(args, ", ")) 387 case shaderir.FieldSelector: 388 return fmt.Sprintf("(%s).%s", glslExpr(&e.Exprs[0]), glslExpr(&e.Exprs[1])) 389 case shaderir.Index: 390 return fmt.Sprintf("(%s)[%s]", glslExpr(&e.Exprs[0]), glslExpr(&e.Exprs[1])) 391 default: 392 return fmt.Sprintf("?(unexpected expr: %d)", e.Type) 393 } 394 } 395 396 idt := strings.Repeat("\t", level+1) 397 for _, s := range block.Stmts { 398 switch s.Type { 399 case shaderir.ExprStmt: 400 lines = append(lines, fmt.Sprintf("%s%s;", idt, glslExpr(&s.Exprs[0]))) 401 case shaderir.BlockStmt: 402 lines = append(lines, idt+"{") 403 lines = append(lines, c.glslBlock(p, topBlock, s.Blocks[0], level+1)...) 404 lines = append(lines, idt+"}") 405 case shaderir.Assign: 406 lhs := s.Exprs[0] 407 rhs := s.Exprs[1] 408 if lhs.Type == shaderir.LocalVariable { 409 if t := p.LocalVariableType(topBlock, block, lhs.Index); t.Main == shaderir.Array { 410 for i := 0; i < t.Length; i++ { 411 lines = append(lines, fmt.Sprintf("%[1]s%[2]s[%[3]d] = %[4]s[%[3]d];", idt, glslExpr(&lhs), i, glslExpr(&rhs))) 412 } 413 continue 414 } 415 } 416 lines = append(lines, fmt.Sprintf("%s%s = %s;", idt, glslExpr(&lhs), glslExpr(&rhs))) 417 case shaderir.Init: 418 lines = append(lines, c.initVariable(p, topBlock, block, s.InitIndex, false, level)...) 419 case shaderir.If: 420 lines = append(lines, fmt.Sprintf("%sif (%s) {", idt, glslExpr(&s.Exprs[0]))) 421 lines = append(lines, c.glslBlock(p, topBlock, s.Blocks[0], level+1)...) 422 if len(s.Blocks) > 1 { 423 lines = append(lines, fmt.Sprintf("%s} else {", idt)) 424 lines = append(lines, c.glslBlock(p, topBlock, s.Blocks[1], level+1)...) 425 } 426 lines = append(lines, fmt.Sprintf("%s}", idt)) 427 case shaderir.For: 428 var ct shaderir.ConstType 429 switch s.ForVarType.Main { 430 case shaderir.Int: 431 ct = shaderir.ConstTypeInt 432 case shaderir.Float: 433 ct = shaderir.ConstTypeFloat 434 } 435 436 v := localVariableName(p, topBlock, block, s.ForVarIndex) 437 var delta string 438 switch val, _ := constant.Float64Val(s.ForDelta); val { 439 case 0: 440 delta = fmt.Sprintf("?(unexpected delta: %v)", s.ForDelta) 441 case 1: 442 delta = fmt.Sprintf("%s++", v) 443 case -1: 444 delta = fmt.Sprintf("%s--", v) 445 default: 446 d := s.ForDelta 447 if val > 0 { 448 delta = fmt.Sprintf("%s += %s", v, constantToNumberLiteral(ct, d)) 449 } else { 450 d = constant.UnaryOp(token.SUB, d, 0) 451 delta = fmt.Sprintf("%s -= %s", v, constantToNumberLiteral(ct, d)) 452 } 453 } 454 var op string 455 switch s.ForOp { 456 case shaderir.LessThanOp, shaderir.LessThanEqualOp, shaderir.GreaterThanOp, shaderir.GreaterThanEqualOp, shaderir.EqualOp, shaderir.NotEqualOp: 457 op = string(s.ForOp) 458 default: 459 op = fmt.Sprintf("?(unexpected op: %s)", string(s.ForOp)) 460 } 461 462 t := s.ForVarType 463 init := constantToNumberLiteral(ct, s.ForInit) 464 end := constantToNumberLiteral(ct, s.ForEnd) 465 t0, t1 := typeString(&t) 466 lines = append(lines, fmt.Sprintf("%sfor (%s %s%s = %s; %s %s %s; %s) {", idt, t0, v, t1, init, v, op, end, delta)) 467 lines = append(lines, c.glslBlock(p, topBlock, s.Blocks[0], level+1)...) 468 lines = append(lines, fmt.Sprintf("%s}", idt)) 469 case shaderir.Continue: 470 lines = append(lines, idt+"continue;") 471 case shaderir.Break: 472 lines = append(lines, idt+"break;") 473 case shaderir.Return: 474 if len(s.Exprs) == 0 { 475 lines = append(lines, idt+"return;") 476 } else { 477 lines = append(lines, fmt.Sprintf("%sreturn %s;", idt, glslExpr(&s.Exprs[0]))) 478 } 479 case shaderir.Discard: 480 lines = append(lines, idt+"discard;") 481 default: 482 lines = append(lines, fmt.Sprintf("%s?(unexpected stmt: %d)", idt, s.Type)) 483 } 484 } 485 486 return lines 487 }