internal/graphicscommand: bug fix: present at the end of the frame explicitly

Before this change, presenting happened when the rendering destination
was the final screen. Now this assumption is wrong as the final screen
might be used in the middle of the commands due to DrawFinalScreen.

Instead, this change adds a new argument `present` to FlushCommands to
present the screen explicitly at the end of the frame.

Closes #2386
This commit is contained in:
Hajime Hoshi 2022-10-15 01:54:43 +09:00
parent d2f6d8593b
commit daf349ab72
6 changed files with 33 additions and 36 deletions

View File

@ -740,7 +740,7 @@ func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
theTemporaryBytes.resetAtFrameEnd()
return restorable.ResolveStaleImages(graphicsDriver)
return restorable.ResolveStaleImages(graphicsDriver, true)
}
func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {

View File

@ -159,15 +159,15 @@ func (q *commandQueue) Enqueue(command command) {
}
// Flush flushes the command queue.
func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics) (err error) {
func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, present bool) (err error) {
runOnRenderingThread(func() {
err = q.flush(graphicsDriver)
err = q.flush(graphicsDriver, present)
})
return
}
// flush must be called the main thread.
func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics) error {
func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, present bool) error {
if len(q.commands) == 0 {
return nil
}
@ -179,7 +179,6 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics) error {
if err := graphicsDriver.Begin(); err != nil {
return err
}
var present bool
cs := q.commands
for len(cs) > 0 {
nv := 0
@ -195,9 +194,6 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics) error {
}
nv += dtc.numVertices()
ne += dtc.numIndices()
if dtc.dst.screen {
present = true
}
}
nc++
}
@ -244,9 +240,10 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics) error {
return nil
}
// FlushCommands flushes the command queue.
func FlushCommands(graphicsDriver graphicsdriver.Graphics) error {
return theCommandQueue.Flush(graphicsDriver)
// FlushCommands flushes the command queue and present the screen.
// If present is true, the current screen is used to present.
func FlushCommands(graphicsDriver graphicsdriver.Graphics, present bool) error {
return theCommandQueue.Flush(graphicsDriver, present)
}
// drawTrianglesCommand represents a drawing command to draw an image on another image.

View File

@ -155,7 +155,7 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte, x
result: buf,
}
theCommandQueue.Enqueue(c)
if err := theCommandQueue.Flush(graphicsDriver); err != nil {
if err := theCommandQueue.Flush(graphicsDriver, false); err != nil {
return err
}
return nil
@ -187,7 +187,7 @@ func (i *Image) IsInvalidated(graphicsDriver graphicsdriver.Graphics) (bool, err
image: i,
}
theCommandQueue.Enqueue(c)
if err := theCommandQueue.Flush(graphicsDriver); err != nil {
if err := theCommandQueue.Flush(graphicsDriver, false); err != nil {
return false, err
}
return c.result, nil

View File

@ -51,11 +51,11 @@ var theImages = &images{
shaders: map[*Shader]struct{}{},
}
// ResolveStaleImages flushes the queued draw commands and resolves
// all stale images.
// ResolveStaleImages flushes the queued draw commands and resolves all stale images.
// If present is true, the current screen is used to present when flushing the commands.
//
// ResolveStaleImages is intended to be called at the end of a frame.
func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error {
func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics, present bool) error {
if debug.IsDebug {
debug.Logf("Internal image sizes:\n")
imgs := make([]*graphicscommand.Image, 0, len(theImages.images))
@ -65,7 +65,7 @@ func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error {
graphicscommand.LogImagesInfo(imgs)
}
if err := graphicscommand.FlushCommands(graphicsDriver); err != nil {
if err := graphicscommand.FlushCommands(graphicsDriver, present); err != nil {
return err
}
if !needsRestoring() {

View File

@ -62,7 +62,7 @@ func TestRestore(t *testing.T) {
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
img0.WritePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, 1, 1)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -81,7 +81,7 @@ func TestRestoreWithoutDraw(t *testing.T) {
// If there is no drawing command on img0, img0 is cleared when restored.
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -147,7 +147,7 @@ func TestRestoreChain(t *testing.T) {
}
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
}
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -199,7 +199,7 @@ func TestRestoreChain2(t *testing.T) {
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[i], w, h, 0, 0), is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
}
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -246,7 +246,7 @@ func TestRestoreOverrideSource(t *testing.T) {
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img2, w, h, 0, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img0.WritePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img0, w, h, 0, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -348,7 +348,7 @@ func TestRestoreComplexGraph(t *testing.T) {
img7.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img3, w, h, 2, 0)
img7.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -447,7 +447,7 @@ func TestRestoreRecursive(t *testing.T) {
}
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img0, w, h, 1, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, w, h, 1, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -509,7 +509,7 @@ func TestWritePixels(t *testing.T) {
}
}
}
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -552,7 +552,7 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) {
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img1.WritePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -596,7 +596,7 @@ func TestDispose(t *testing.T) {
img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, 1, 1, 0, 0), is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img1.Dispose()
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -728,7 +728,7 @@ func TestWritePixelsOnly(t *testing.T) {
}
}
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -823,7 +823,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
dst.WritePixels(make([]byte, 4*2*2), 0, 0, 2, 2)
// WritePixels for a part of image doesn't panic.
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -929,7 +929,7 @@ func TestMutateSlices(t *testing.T) {
for i := range is {
is[i] = 0
}
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -1081,7 +1081,7 @@ func TestOverlappedPixels(t *testing.T) {
}
}
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {

View File

@ -66,7 +66,7 @@ func TestShader(t *testing.T) {
}
img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(nil, 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, s, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -102,7 +102,7 @@ func TestShaderChain(t *testing.T) {
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[i], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, s, nil, false)
}
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -142,7 +142,7 @@ func TestShaderMultipleSources(t *testing.T) {
// Clear one of the sources after DrawTriangles. dst should not be affected.
clearImage(srcs[0], 1, 1)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -183,7 +183,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
// Clear one of the sources after DrawTriangles. dst should not be affected.
clearImage(srcs[0], 3, 1)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
@ -214,7 +214,7 @@ func TestShaderDispose(t *testing.T) {
// stale.
s.Dispose()
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {