diff --git a/examples/mrt/main.go b/examples/mrt/main.go index 5ad46f979..15c34d949 100644 --- a/examples/mrt/main.go +++ b/examples/mrt/main.go @@ -15,11 +15,13 @@ package main import ( + "fmt" "image" _ "image/jpeg" "log" "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" ) const ( @@ -71,11 +73,9 @@ func init() { } type Game struct { - count int } func (g *Game) Update() error { - g.count++ return nil } @@ -114,6 +114,8 @@ func (g *Game) Draw(screen *ebiten.Image) { opts.GeoM.Reset() opts.GeoM.Translate(dstSize, dstSize) screen.DrawImage(dsts[3], opts) + + ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %.2f", ebiten.ActualFPS())) } func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { @@ -122,9 +124,10 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { func main() { ebiten.SetWindowSize(screenWidth, screenHeight) + ebiten.SetVsyncEnabled(false) ebiten.SetWindowTitle("MRT (Ebitengine Demo)") if err := ebiten.RunGameWithOptions(&Game{}, &ebiten.RunGameOptions{ - GraphicsLibrary: ebiten.GraphicsLibraryDirectX, + GraphicsLibrary: ebiten.GraphicsLibraryOpenGL, }); err != nil { log.Fatal(err) } diff --git a/internal/graphicsdriver/opengl/context.go b/internal/graphicsdriver/opengl/context.go index 57bc4bffb..0436746f9 100644 --- a/internal/graphicsdriver/opengl/context.go +++ b/internal/graphicsdriver/opengl/context.go @@ -396,7 +396,7 @@ func (c *context) newShader(shaderType uint32, source string) (shader, error) { return shader(s), nil } -func (c *context) newProgram(shaders []shader, attributes []string) (program, error) { +func (c *context) newProgram(shaders []shader, attributes, fragData []string) (program, error) { p := c.ctx.CreateProgram() if p == 0 { return 0, errors.New("opengl: glCreateProgram failed") @@ -410,6 +410,10 @@ func (c *context) newProgram(shaders []shader, attributes []string) (program, er c.ctx.BindAttribLocation(p, uint32(i), name) } + for i, name := range fragData { + c.ctx.BindFragDataLocation(p, uint32(i), name) + } + c.ctx.LinkProgram(p) if c.ctx.GetProgrami(p, gl.LINK_STATUS) == gl.FALSE { info := c.ctx.GetProgramInfoLog(p) diff --git a/internal/graphicsdriver/opengl/gl/debug.go b/internal/graphicsdriver/opengl/gl/debug.go index c1fd7a908..7c77a071e 100644 --- a/internal/graphicsdriver/opengl/gl/debug.go +++ b/internal/graphicsdriver/opengl/gl/debug.go @@ -61,6 +61,14 @@ func (d *DebugContext) BindBuffer(arg0 uint32, arg1 uint32) { } } +func (d *DebugContext) BindFragDataLocation(arg0 uint32, arg1 uint32, arg2 string) { + d.Context.BindFragDataLocation(arg0, arg1, arg2) + fmt.Fprintln(os.Stderr, "BindFragDataLocation") + if e := d.Context.GetError(); e != NO_ERROR { + panic(fmt.Sprintf("gl: GetError() returned %d at BindFragDataLocation", e)) + } +} + func (d *DebugContext) BindFramebuffer(arg0 uint32, arg1 uint32) { d.Context.BindFramebuffer(arg0, arg1) fmt.Fprintln(os.Stderr, "BindFramebuffer") diff --git a/internal/graphicsdriver/opengl/gl/default_purego.go b/internal/graphicsdriver/opengl/gl/default_purego.go index ac7d219df..b4b9b5e5a 100644 --- a/internal/graphicsdriver/opengl/gl/default_purego.go +++ b/internal/graphicsdriver/opengl/gl/default_purego.go @@ -28,6 +28,7 @@ type defaultContext struct { gpAttachShader uintptr gpBindAttribLocation uintptr gpBindBuffer uintptr + gpBindFragDataLocation uintptr gpBindFramebuffer uintptr gpBindRenderbuffer uintptr gpBindTexture uintptr @@ -140,6 +141,12 @@ func (c *defaultContext) BindBuffer(target uint32, buffer uint32) { purego.SyscallN(c.gpBindBuffer, uintptr(target), uintptr(buffer)) } +func (c *defaultContext) BindFragDataLocation(program uint32, index uint32, name string) { + cname, free := cStr(name) + defer free() + purego.SyscallN(c.gpBindFragDataLocation, uintptr(program), uintptr(index), uintptr(unsafe.Pointer(cname))) +} + func (c *defaultContext) BindFramebuffer(target uint32, framebuffer uint32) { purego.SyscallN(c.gpBindFramebuffer, uintptr(target), uintptr(framebuffer)) } @@ -483,6 +490,7 @@ func (c *defaultContext) LoadFunctions() error { c.gpAttachShader = g.get("glAttachShader") c.gpBindAttribLocation = g.get("glBindAttribLocation") c.gpBindBuffer = g.get("glBindBuffer") + c.gpBindFragDataLocation = g.get("glBindFragDataLocation") c.gpBindFramebuffer = g.get("glBindFramebuffer") c.gpBindRenderbuffer = g.get("glBindRenderbuffer") c.gpBindTexture = g.get("glBindTexture") diff --git a/internal/graphicsdriver/opengl/gl/interface.go b/internal/graphicsdriver/opengl/gl/interface.go index 6565d2a30..1825116fd 100644 --- a/internal/graphicsdriver/opengl/gl/interface.go +++ b/internal/graphicsdriver/opengl/gl/interface.go @@ -31,6 +31,7 @@ type Context interface { AttachShader(program uint32, shader uint32) BindAttribLocation(program uint32, index uint32, name string) BindBuffer(target uint32, buffer uint32) + BindFragDataLocation(program uint32, index uint32, name string) BindFramebuffer(target uint32, framebuffer uint32) BindRenderbuffer(target uint32, renderbuffer uint32) BindTexture(target uint32, texture uint32) diff --git a/internal/graphicsdriver/opengl/program.go b/internal/graphicsdriver/opengl/program.go index fadb6fd9c..9ffad216b 100644 --- a/internal/graphicsdriver/opengl/program.go +++ b/internal/graphicsdriver/opengl/program.go @@ -265,7 +265,7 @@ func (g *Graphics) colorBufferVariableName(idx int) string { if g.colorBufferVariableNameCache == nil { g.colorBufferVariableNameCache = map[int]string{} } - name := fmt.Sprintf("gl_FragData[%d]", idx) + name := fmt.Sprintf("fragColor%d", idx) g.colorBufferVariableNameCache[idx] = name return name } diff --git a/internal/graphicsdriver/opengl/shader.go b/internal/graphicsdriver/opengl/shader.go index 8f8890f59..39ced1988 100644 --- a/internal/graphicsdriver/opengl/shader.go +++ b/internal/graphicsdriver/opengl/shader.go @@ -69,7 +69,11 @@ func (s *Shader) compile() error { } defer s.graphics.context.ctx.DeleteShader(uint32(fs)) - p, err := s.graphics.context.newProgram([]shader{vs, fs}, theArrayBufferLayout.names()) + colorNames := make([]string, s.ir.ColorsOutCount) + for i := range colorNames { + colorNames[i] = s.graphics.colorBufferVariableName(i) + } + p, err := s.graphics.context.newProgram([]shader{vs, fs}, theArrayBufferLayout.names(), colorNames) if err != nil { return err } diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 0bd326aef..b58d101ed 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -822,7 +822,7 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool return function{}, false } - // The first out-param is treated as gl_FragColor in GLSL. + // The first out-param is treated as fragColor0 in GLSL. for i := range outParams { if outParams[i].typ.Main != shaderir.Vec4 { cs.addError(d.Pos(), "fragment entry point must only have vec4 return values for colors") diff --git a/internal/shaderir/glsl/glsl.go b/internal/shaderir/glsl/glsl.go index 56e89e08e..56611d8e7 100644 --- a/internal/shaderir/glsl/glsl.go +++ b/internal/shaderir/glsl/glsl.go @@ -86,8 +86,7 @@ precision highp int; #define lowp #define mediump #define highp -#endif -` +#endif` if version == GLSLVersionDefault { prelude += "\n\n" + utilFunctions } @@ -230,6 +229,9 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh fslines = append(fslines, fmt.Sprintf("in %s;", c.varDecl(p, &t, fmt.Sprintf("V%d", i)))) } } + for i := 0; i < p.ColorsOutCount; i++ { + fslines = append(fslines, fmt.Sprintf("out vec4 fragColor%d;", i)) + } var funcs []*shaderir.Func if p.VertexFunc.Block != nil { @@ -291,6 +293,8 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh vs = strings.TrimSpace(vs) + "\n" fs = strings.TrimSpace(fs) + "\n" + fmt.Println("FS:", fs) + return vs, fs } @@ -419,7 +423,7 @@ func (c *compileContext) localVariableName(p *shaderir.Program, topBlock *shader case idx < nv+1: return fmt.Sprintf("V%d", idx-1) default: - return fmt.Sprintf("gl_FragData[%d]", idx-(nv+1)) + return fmt.Sprintf("fragColor%d", idx-(nv+1)) } default: return fmt.Sprintf("l%d", idx) diff --git a/internal/shaderir/hlsl/hlsl.go b/internal/shaderir/hlsl/hlsl.go index a01557338..85b820d26 100644 --- a/internal/shaderir/hlsl/hlsl.go +++ b/internal/shaderir/hlsl/hlsl.go @@ -607,11 +607,6 @@ func adjustProgram(p *shaderir.Program) *shaderir.Program { 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 { @@ -659,7 +654,8 @@ func adjustProgram(p *shaderir.Program) *shaderir.Program { // Replace the entry point with just calling the new function. stmts := []shaderir.Stmt{ { - // Return: This will be replaced with assignment to gl_FragColor. + // Return: This will be replaced with a call to the new function. + // Then the output structure containing colors will be returned. Type: shaderir.Return, Exprs: []shaderir.Expr{ // The function call