diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 434ba509d..75901a90e 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -61,14 +61,15 @@ func (p *drawTrianglesCommandPool) put(v *drawTrianglesCommand) { // drawTrianglesCommand represents a drawing command to draw an image on another image. type drawTrianglesCommand struct { - dst *Image - srcs [graphics.ShaderSrcImageCount]*Image - vertices []float32 - blend graphicsdriver.Blend - dstRegions []graphicsdriver.DstRegion - shader *Shader - uniforms []uint32 - fillRule graphicsdriver.FillRule + dst *Image + srcs [graphics.ShaderSrcImageCount]*Image + vertices []float32 + blend graphicsdriver.Blend + dstRegions []graphicsdriver.DstRegion + shader *Shader + uniforms []uint32 + fillRule graphicsdriver.FillRule + firstCaller string } func (c *drawTrianglesCommand) String() string { @@ -116,7 +117,11 @@ func (c *drawTrianglesCommand) String() string { shader += " (" + c.shader.name + ")" } - return fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], num of dst regions: %d, num of indices: %d, blend: %s, fill rule: %s, shader: %s", dst, strings.Join(srcstrs[:], ", "), len(c.dstRegions), c.numIndices(), blend, c.fillRule, shader) + str := fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], num of dst regions: %d, num of indices: %d, blend: %s, fill rule: %s, shader: %s", dst, strings.Join(srcstrs[:], ", "), len(c.dstRegions), c.numIndices(), blend, c.fillRule, shader) + if c.firstCaller != "" { + str += "\n first-caller: " + c.firstCaller + } + return str } // Exec executes the drawTrianglesCommand. diff --git a/internal/graphicscommand/commandqueue.go b/internal/graphicscommand/commandqueue.go index 8bb07b854..4e01b0cbc 100644 --- a/internal/graphicscommand/commandqueue.go +++ b/internal/graphicscommand/commandqueue.go @@ -18,6 +18,8 @@ import ( "fmt" "image" "math" + "runtime" + "strings" "sync" "sync/atomic" @@ -162,6 +164,23 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh c.shader = shader c.uniforms = uniforms c.fillRule = fillRule + if debug.IsDebug { + // Get the root caller of this function. + // Relying on a caller stacktrace is very fragile, but this is fine as this is only for debugging. + for i := 0; ; i++ { + _, file, _, ok := runtime.Caller(i) + if !ok { + break + } + if !strings.HasSuffix(file, "/ebiten/image.go") { + continue + } + if _, file, line, ok := runtime.Caller(i + 1); ok { + c.firstCaller = fmt.Sprintf("%s:%d", file, line) + } + break + } + } q.commands = append(q.commands, c) } @@ -294,7 +313,15 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo if err := c.Exec(q, graphicsDriver, indexOffset); err != nil { return err } - logger.FrameLogf(" %s\n", c) + str := c.String() + for { + head, tail, ok := strings.Cut(str, "\n") + logger.FrameLogf(" %s\n", head) + if !ok { + break + } + str = tail + } // TODO: indexOffset should be reset if the command type is different // from the previous one. This fix is needed when another drawing command is // introduced than drawTrianglesCommand.