glsl.go (18532B)
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 "sort" 23 "strings" 24 25 "github.com/hajimehoshi/ebiten/v2/internal/shaderir" 26 ) 27 28 type GLSLVersion int 29 30 const ( 31 GLSLVersionDefault GLSLVersion = iota 32 GLSLVersionES100 33 GLSLVersionES300 34 ) 35 36 func VertexPrelude(version GLSLVersion) string { 37 if version == GLSLVersionES300 { 38 return `#version 300 es` 39 } 40 return "" 41 } 42 43 func FragmentPrelude(version GLSLVersion) string { 44 var prefix string 45 switch version { 46 case GLSLVersionES100: 47 prefix = `#extension GL_OES_standard_derivatives : enable` + "\n\n" 48 case GLSLVersionES300: 49 prefix = `#version 300 es` + "\n\n" 50 } 51 return prefix + `#if defined(GL_ES) 52 precision highp float; 53 #else 54 #define lowp 55 #define mediump 56 #define highp 57 #endif` 58 } 59 60 type compileContext struct { 61 version GLSLVersion 62 structNames map[string]string 63 structTypes []shaderir.Type 64 } 65 66 func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) string { 67 if t.Main != shaderir.Struct { 68 panic("glsl: the given type at structName must be a struct") 69 } 70 s := t.String() 71 if n, ok := c.structNames[s]; ok { 72 return n 73 } 74 n := fmt.Sprintf("S%d", len(c.structNames)) 75 c.structNames[s] = n 76 c.structTypes = append(c.structTypes, *t) 77 return n 78 } 79 80 func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentShader string) { 81 c := &compileContext{ 82 version: version, 83 structNames: map[string]string{}, 84 } 85 86 indexToFunc := map[int]*shaderir.Func{} 87 for _, f := range p.Funcs { 88 f := f 89 indexToFunc[f.Index] = &f 90 } 91 92 // Vertex func 93 var vslines []string 94 { 95 vslines = append(vslines, strings.Split(VertexPrelude(version), "\n")...) 96 vslines = append(vslines, "{{.Structs}}") 97 if len(p.Uniforms) > 0 || p.TextureNum > 0 || len(p.Attributes) > 0 || len(p.Varyings) > 0 { 98 vslines = append(vslines, "") 99 for i, t := range p.Uniforms { 100 vslines = append(vslines, fmt.Sprintf("uniform %s;", c.glslVarDecl(p, &t, fmt.Sprintf("U%d", i)))) 101 } 102 for i := 0; i < p.TextureNum; i++ { 103 vslines = append(vslines, fmt.Sprintf("uniform sampler2D T%d;", i)) 104 } 105 for i, t := range p.Attributes { 106 keyword := "attribute" 107 if version == GLSLVersionES300 { 108 keyword = "in" 109 } 110 vslines = append(vslines, fmt.Sprintf("%s %s;", keyword, c.glslVarDecl(p, &t, fmt.Sprintf("A%d", i)))) 111 } 112 for i, t := range p.Varyings { 113 keyword := "varying" 114 if version == GLSLVersionES300 { 115 keyword = "out" 116 } 117 vslines = append(vslines, fmt.Sprintf("%s %s;", keyword, c.glslVarDecl(p, &t, fmt.Sprintf("V%d", i)))) 118 } 119 } 120 121 var funcs []*shaderir.Func 122 if p.VertexFunc.Block != nil { 123 indices := p.ReferredFuncIndicesInVertexShader() 124 sort.Ints(indices) 125 funcs = make([]*shaderir.Func, 0, len(indices)) 126 for _, idx := range indices { 127 funcs = append(funcs, indexToFunc[idx]) 128 } 129 } else { 130 // When a vertex entry point is not defined, allow to put all the functions. This is useful for testing. 131 funcs = make([]*shaderir.Func, 0, len(p.Funcs)) 132 for _, f := range p.Funcs { 133 f := f 134 funcs = append(funcs, &f) 135 } 136 } 137 if len(funcs) > 0 { 138 vslines = append(vslines, "") 139 for _, f := range funcs { 140 vslines = append(vslines, c.glslFunc(p, f, true)...) 141 } 142 for _, f := range funcs { 143 if len(vslines) > 0 && vslines[len(vslines)-1] != "" { 144 vslines = append(vslines, "") 145 } 146 vslines = append(vslines, c.glslFunc(p, f, false)...) 147 } 148 } 149 150 // Add a dummy function to just touch uniform array variable's elements (#1754). 151 // Without this, the first elements of a uniform array might not be initialized correctly on some environments. 152 var touchedUniforms []string 153 for i, t := range p.Uniforms { 154 if t.Main != shaderir.Array { 155 continue 156 } 157 if t.Length <= 1 { 158 continue 159 } 160 str := fmt.Sprintf("U%d[%d]", i, t.Length-1) 161 switch t.Sub[0].Main { 162 case shaderir.Vec2, shaderir.Vec3, shaderir.Vec4: 163 str += ".x" 164 case shaderir.Mat2, shaderir.Mat3, shaderir.Mat4: 165 str += "[0][0]" 166 } 167 str = "float(" + str + ")" 168 touchedUniforms = append(touchedUniforms, str) 169 } 170 171 var touchUniformsFunc []string 172 if len(touchedUniforms) > 0 { 173 touchUniformsFunc = append(touchUniformsFunc, "float touchUniforms() {") 174 touchUniformsFunc = append(touchUniformsFunc, fmt.Sprintf("\treturn %s;", strings.Join(touchedUniforms, " + "))) 175 touchUniformsFunc = append(touchUniformsFunc, "}") 176 177 } 178 179 if p.VertexFunc.Block != nil && len(p.VertexFunc.Block.Stmts) > 0 { 180 if len(touchUniformsFunc) > 0 { 181 vslines = append(vslines, "") 182 vslines = append(vslines, touchUniformsFunc...) 183 } 184 vslines = append(vslines, "") 185 vslines = append(vslines, "void main(void) {") 186 if len(touchUniformsFunc) > 0 { 187 vslines = append(vslines, "\ttouchUniforms();") 188 } 189 vslines = append(vslines, c.glslBlock(p, p.VertexFunc.Block, p.VertexFunc.Block, 0)...) 190 vslines = append(vslines, "}") 191 } 192 } 193 194 // Fragment func 195 var fslines []string 196 { 197 fslines = append(fslines, strings.Split(FragmentPrelude(version), "\n")...) 198 fslines = append(fslines, "", "{{.Structs}}") 199 if len(p.Uniforms) > 0 || p.TextureNum > 0 || len(p.Varyings) > 0 { 200 fslines = append(fslines, "") 201 for i, t := range p.Uniforms { 202 fslines = append(fslines, fmt.Sprintf("uniform %s;", c.glslVarDecl(p, &t, fmt.Sprintf("U%d", i)))) 203 } 204 for i := 0; i < p.TextureNum; i++ { 205 fslines = append(fslines, fmt.Sprintf("uniform sampler2D T%d;", i)) 206 } 207 for i, t := range p.Varyings { 208 keyword := "varying" 209 if version == GLSLVersionES300 { 210 keyword = "in" 211 } 212 fslines = append(fslines, fmt.Sprintf("%s %s;", keyword, c.glslVarDecl(p, &t, fmt.Sprintf("V%d", i)))) 213 } 214 } 215 if version == GLSLVersionES300 { 216 fslines = append(fslines, "out vec4 fragColor;") 217 } 218 219 var funcs []*shaderir.Func 220 if p.VertexFunc.Block != nil { 221 indices := p.ReferredFuncIndicesInFragmentShader() 222 sort.Ints(indices) 223 funcs = make([]*shaderir.Func, 0, len(indices)) 224 for _, idx := range indices { 225 funcs = append(funcs, indexToFunc[idx]) 226 } 227 } else { 228 // When a fragment entry point is not defined, allow to put all the functions. This is useful for testing. 229 funcs = make([]*shaderir.Func, 0, len(p.Funcs)) 230 for _, f := range p.Funcs { 231 f := f 232 funcs = append(funcs, &f) 233 } 234 } 235 if len(funcs) > 0 { 236 fslines = append(fslines, "") 237 for _, f := range funcs { 238 fslines = append(fslines, c.glslFunc(p, f, true)...) 239 } 240 for _, f := range funcs { 241 if len(fslines) > 0 && fslines[len(fslines)-1] != "" { 242 fslines = append(fslines, "") 243 } 244 fslines = append(fslines, c.glslFunc(p, f, false)...) 245 } 246 } 247 248 if p.FragmentFunc.Block != nil && len(p.FragmentFunc.Block.Stmts) > 0 { 249 fslines = append(fslines, "") 250 fslines = append(fslines, "void main(void) {") 251 fslines = append(fslines, c.glslBlock(p, p.FragmentFunc.Block, p.FragmentFunc.Block, 0)...) 252 fslines = append(fslines, "}") 253 } 254 } 255 256 vs := strings.Join(vslines, "\n") 257 fs := strings.Join(fslines, "\n") 258 259 // Struct types are determined after converting the program. 260 if len(c.structTypes) > 0 { 261 var stlines []string 262 for i, t := range c.structTypes { 263 stlines = append(stlines, fmt.Sprintf("struct S%d {", i)) 264 for j, st := range t.Sub { 265 stlines = append(stlines, fmt.Sprintf("\t%s;", c.glslVarDecl(p, &st, fmt.Sprintf("M%d", j)))) 266 } 267 stlines = append(stlines, "};") 268 } 269 st := strings.Join(stlines, "\n") 270 vs = strings.ReplaceAll(vs, "{{.Structs}}", st) 271 fs = strings.ReplaceAll(fs, "{{.Structs}}", st) 272 } else { 273 vs = strings.ReplaceAll(vs, "{{.Structs}}", "") 274 fs = strings.ReplaceAll(fs, "{{.Structs}}", "") 275 } 276 277 nls := regexp.MustCompile(`\n\n+`) 278 vs = nls.ReplaceAllString(vs, "\n\n") 279 fs = nls.ReplaceAllString(fs, "\n\n") 280 281 vs = strings.TrimSpace(vs) + "\n" 282 fs = strings.TrimSpace(fs) + "\n" 283 284 return vs, fs 285 } 286 287 func (c *compileContext) glslType(p *shaderir.Program, t *shaderir.Type) (string, string) { 288 switch t.Main { 289 case shaderir.None: 290 return "void", "" 291 case shaderir.Struct: 292 return c.structName(p, t), "" 293 default: 294 return typeString(t) 295 } 296 } 297 298 func (c *compileContext) glslVarDecl(p *shaderir.Program, t *shaderir.Type, varname string) string { 299 switch t.Main { 300 case shaderir.None: 301 return "?(none)" 302 case shaderir.Struct: 303 return fmt.Sprintf("%s %s", c.structName(p, t), varname) 304 default: 305 t0, t1 := typeString(t) 306 return fmt.Sprintf("%s %s%s", t0, varname, t1) 307 } 308 } 309 310 func (c *compileContext) glslVarInit(p *shaderir.Program, t *shaderir.Type) string { 311 switch t.Main { 312 case shaderir.None: 313 return "?(none)" 314 case shaderir.Array: 315 init := c.glslVarInit(p, &t.Sub[0]) 316 es := make([]string, 0, t.Length) 317 for i := 0; i < t.Length; i++ { 318 es = append(es, init) 319 } 320 t0, t1 := typeString(t) 321 return fmt.Sprintf("%s%s(%s)", t0, t1, strings.Join(es, ", ")) 322 case shaderir.Struct: 323 panic("not implemented") 324 case shaderir.Bool: 325 return "false" 326 case shaderir.Int: 327 return "0" 328 case shaderir.Float, shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4: 329 return fmt.Sprintf("%s(0)", basicTypeString(t.Main)) 330 default: 331 t0, t1 := c.glslType(p, t) 332 panic(fmt.Sprintf("?(unexpected type: %s%s)", t0, t1)) 333 } 334 } 335 336 func (c *compileContext) glslFunc(p *shaderir.Program, f *shaderir.Func, prototype bool) []string { 337 var args []string 338 var idx int 339 for _, t := range f.InParams { 340 args = append(args, "in "+c.glslVarDecl(p, &t, fmt.Sprintf("l%d", idx))) 341 idx++ 342 } 343 for _, t := range f.OutParams { 344 args = append(args, "out "+c.glslVarDecl(p, &t, fmt.Sprintf("l%d", idx))) 345 idx++ 346 } 347 argsstr := "void" 348 if len(args) > 0 { 349 argsstr = strings.Join(args, ", ") 350 } 351 352 t0, t1 := c.glslType(p, &f.Return) 353 sig := fmt.Sprintf("%s%s F%d(%s)", t0, t1, f.Index, argsstr) 354 355 var lines []string 356 if prototype { 357 lines = append(lines, fmt.Sprintf("%s;", sig)) 358 return lines 359 } 360 lines = append(lines, fmt.Sprintf("%s {", sig)) 361 lines = append(lines, c.glslBlock(p, f.Block, f.Block, 0)...) 362 lines = append(lines, "}") 363 364 return lines 365 } 366 367 func constantToNumberLiteral(t shaderir.ConstType, v constant.Value) string { 368 switch t { 369 case shaderir.ConstTypeNone: 370 if v.Kind() == constant.Bool { 371 if constant.BoolVal(v) { 372 return "true" 373 } 374 return "false" 375 } 376 fallthrough 377 case shaderir.ConstTypeFloat: 378 if i := constant.ToInt(v); i.Kind() == constant.Int { 379 x, _ := constant.Int64Val(i) 380 return fmt.Sprintf("%d.0", x) 381 } 382 if i := constant.ToFloat(v); i.Kind() == constant.Float { 383 x, _ := constant.Float64Val(i) 384 return fmt.Sprintf("%.10e", x) 385 } 386 case shaderir.ConstTypeInt: 387 if i := constant.ToInt(v); i.Kind() == constant.Int { 388 x, _ := constant.Int64Val(i) 389 return fmt.Sprintf("%d", x) 390 } 391 } 392 return fmt.Sprintf("?(unexpected literal: %s)", v) 393 } 394 395 func (c *compileContext) localVariableName(p *shaderir.Program, topBlock, block *shaderir.Block, idx int) string { 396 switch topBlock { 397 case p.VertexFunc.Block: 398 na := len(p.Attributes) 399 nv := len(p.Varyings) 400 switch { 401 case idx < na: 402 return fmt.Sprintf("A%d", idx) 403 case idx == na: 404 return "gl_Position" 405 case idx < na+nv+1: 406 return fmt.Sprintf("V%d", idx-na-1) 407 default: 408 return fmt.Sprintf("l%d", idx-(na+nv+1)) 409 } 410 case p.FragmentFunc.Block: 411 nv := len(p.Varyings) 412 switch { 413 case idx == 0: 414 return "gl_FragCoord" 415 case idx < nv+1: 416 return fmt.Sprintf("V%d", idx-1) 417 case idx == nv+1: 418 if c.version == GLSLVersionES300 { 419 return "fragColor" 420 } 421 return "gl_FragColor" 422 default: 423 return fmt.Sprintf("l%d", idx-(nv+2)) 424 } 425 default: 426 return fmt.Sprintf("l%d", idx) 427 } 428 } 429 430 func (c *compileContext) initVariable(p *shaderir.Program, topBlock, block *shaderir.Block, index int, decl bool, level int) []string { 431 idt := strings.Repeat("\t", level+1) 432 name := c.localVariableName(p, topBlock, block, index) 433 t := p.LocalVariableType(topBlock, block, index) 434 435 var lines []string 436 switch t.Main { 437 case shaderir.Array: 438 if decl { 439 lines = append(lines, fmt.Sprintf("%s%s;", idt, c.glslVarDecl(p, &t, name))) 440 } 441 init := c.glslVarInit(p, &t.Sub[0]) 442 for i := 0; i < t.Length; i++ { 443 lines = append(lines, fmt.Sprintf("%s%s[%d] = %s;", idt, name, i, init)) 444 } 445 case shaderir.None: 446 // The type is None e.g., when the variable is a for-loop counter. 447 default: 448 if decl { 449 lines = append(lines, fmt.Sprintf("%s%s = %s;", idt, c.glslVarDecl(p, &t, name), c.glslVarInit(p, &t))) 450 } else { 451 lines = append(lines, fmt.Sprintf("%s%s = %s;", idt, name, c.glslVarInit(p, &t))) 452 } 453 } 454 return lines 455 } 456 457 func (c *compileContext) glslBlock(p *shaderir.Program, topBlock, block *shaderir.Block, level int) []string { 458 if block == nil { 459 return nil 460 } 461 462 var lines []string 463 for i := range block.LocalVars { 464 lines = append(lines, c.initVariable(p, topBlock, block, block.LocalVarIndexOffset+i, true, level)...) 465 } 466 467 var glslExpr func(e *shaderir.Expr) string 468 glslExpr = func(e *shaderir.Expr) string { 469 switch e.Type { 470 case shaderir.NumberExpr: 471 return constantToNumberLiteral(e.ConstType, e.Const) 472 case shaderir.UniformVariable: 473 return fmt.Sprintf("U%d", e.Index) 474 case shaderir.TextureVariable: 475 return fmt.Sprintf("T%d", e.Index) 476 case shaderir.LocalVariable: 477 return c.localVariableName(p, topBlock, block, e.Index) 478 case shaderir.StructMember: 479 return fmt.Sprintf("M%d", e.Index) 480 case shaderir.BuiltinFuncExpr: 481 return c.builtinFuncString(e.BuiltinFunc) 482 case shaderir.SwizzlingExpr: 483 if !shaderir.IsValidSwizzling(e.Swizzling) { 484 return fmt.Sprintf("?(unexpected swizzling: %s)", e.Swizzling) 485 } 486 return e.Swizzling 487 case shaderir.FunctionExpr: 488 return fmt.Sprintf("F%d", e.Index) 489 case shaderir.Unary: 490 var op string 491 switch e.Op { 492 case shaderir.Add, shaderir.Sub, shaderir.NotOp: 493 op = string(e.Op) 494 default: 495 op = fmt.Sprintf("?(unexpected op: %s)", string(e.Op)) 496 } 497 return fmt.Sprintf("%s(%s)", op, glslExpr(&e.Exprs[0])) 498 case shaderir.Binary: 499 return fmt.Sprintf("(%s) %s (%s)", glslExpr(&e.Exprs[0]), e.Op, glslExpr(&e.Exprs[1])) 500 case shaderir.Selection: 501 return fmt.Sprintf("(%s) ? (%s) : (%s)", glslExpr(&e.Exprs[0]), glslExpr(&e.Exprs[1]), glslExpr(&e.Exprs[2])) 502 case shaderir.Call: 503 var args []string 504 for _, exp := range e.Exprs[1:] { 505 args = append(args, glslExpr(&exp)) 506 } 507 // Using parentheses at the callee is illegal. 508 return fmt.Sprintf("%s(%s)", glslExpr(&e.Exprs[0]), strings.Join(args, ", ")) 509 case shaderir.FieldSelector: 510 return fmt.Sprintf("(%s).%s", glslExpr(&e.Exprs[0]), glslExpr(&e.Exprs[1])) 511 case shaderir.Index: 512 return fmt.Sprintf("(%s)[%s]", glslExpr(&e.Exprs[0]), glslExpr(&e.Exprs[1])) 513 default: 514 return fmt.Sprintf("?(unexpected expr: %d)", e.Type) 515 } 516 } 517 518 idt := strings.Repeat("\t", level+1) 519 for _, s := range block.Stmts { 520 switch s.Type { 521 case shaderir.ExprStmt: 522 lines = append(lines, fmt.Sprintf("%s%s;", idt, glslExpr(&s.Exprs[0]))) 523 case shaderir.BlockStmt: 524 lines = append(lines, idt+"{") 525 lines = append(lines, c.glslBlock(p, topBlock, s.Blocks[0], level+1)...) 526 lines = append(lines, idt+"}") 527 case shaderir.Assign: 528 lhs := s.Exprs[0] 529 rhs := s.Exprs[1] 530 if lhs.Type == shaderir.LocalVariable { 531 if t := p.LocalVariableType(topBlock, block, lhs.Index); t.Main == shaderir.Array { 532 for i := 0; i < t.Length; i++ { 533 lines = append(lines, fmt.Sprintf("%[1]s%[2]s[%[3]d] = %[4]s[%[3]d];", idt, glslExpr(&lhs), i, glslExpr(&rhs))) 534 } 535 continue 536 } 537 } 538 lines = append(lines, fmt.Sprintf("%s%s = %s;", idt, glslExpr(&lhs), glslExpr(&rhs))) 539 case shaderir.Init: 540 lines = append(lines, c.initVariable(p, topBlock, block, s.InitIndex, false, level)...) 541 case shaderir.If: 542 lines = append(lines, fmt.Sprintf("%sif (%s) {", idt, glslExpr(&s.Exprs[0]))) 543 lines = append(lines, c.glslBlock(p, topBlock, s.Blocks[0], level+1)...) 544 if len(s.Blocks) > 1 { 545 lines = append(lines, fmt.Sprintf("%s} else {", idt)) 546 lines = append(lines, c.glslBlock(p, topBlock, s.Blocks[1], level+1)...) 547 } 548 lines = append(lines, fmt.Sprintf("%s}", idt)) 549 case shaderir.For: 550 var ct shaderir.ConstType 551 switch s.ForVarType.Main { 552 case shaderir.Int: 553 ct = shaderir.ConstTypeInt 554 case shaderir.Float: 555 ct = shaderir.ConstTypeFloat 556 } 557 558 v := c.localVariableName(p, topBlock, block, s.ForVarIndex) 559 var delta string 560 switch val, _ := constant.Float64Val(s.ForDelta); val { 561 case 0: 562 delta = fmt.Sprintf("?(unexpected delta: %v)", s.ForDelta) 563 case 1: 564 delta = fmt.Sprintf("%s++", v) 565 case -1: 566 delta = fmt.Sprintf("%s--", v) 567 default: 568 d := s.ForDelta 569 if val > 0 { 570 delta = fmt.Sprintf("%s += %s", v, constantToNumberLiteral(ct, d)) 571 } else { 572 d = constant.UnaryOp(token.SUB, d, 0) 573 delta = fmt.Sprintf("%s -= %s", v, constantToNumberLiteral(ct, d)) 574 } 575 } 576 var op string 577 switch s.ForOp { 578 case shaderir.LessThanOp, shaderir.LessThanEqualOp, shaderir.GreaterThanOp, shaderir.GreaterThanEqualOp, shaderir.EqualOp, shaderir.NotEqualOp: 579 op = string(s.ForOp) 580 default: 581 op = fmt.Sprintf("?(unexpected op: %s)", string(s.ForOp)) 582 } 583 584 t := s.ForVarType 585 init := constantToNumberLiteral(ct, s.ForInit) 586 end := constantToNumberLiteral(ct, s.ForEnd) 587 t0, t1 := typeString(&t) 588 lines = append(lines, fmt.Sprintf("%sfor (%s %s%s = %s; %s %s %s; %s) {", idt, t0, v, t1, init, v, op, end, delta)) 589 lines = append(lines, c.glslBlock(p, topBlock, s.Blocks[0], level+1)...) 590 lines = append(lines, fmt.Sprintf("%s}", idt)) 591 case shaderir.Continue: 592 lines = append(lines, idt+"continue;") 593 case shaderir.Break: 594 lines = append(lines, idt+"break;") 595 case shaderir.Return: 596 if len(s.Exprs) == 0 { 597 lines = append(lines, idt+"return;") 598 } else { 599 lines = append(lines, fmt.Sprintf("%sreturn %s;", idt, glslExpr(&s.Exprs[0]))) 600 } 601 case shaderir.Discard: 602 lines = append(lines, idt+"discard;") 603 default: 604 lines = append(lines, fmt.Sprintf("%s?(unexpected stmt: %d)", idt, s.Type)) 605 } 606 } 607 608 return lines 609 }