internal/graphicsdriver/directx: use DXGI_PRESENT_TEST when the screen is invisible

This change also reduces FPS to about 10 when the screen is invisible
in order to save CPU power.

Closes #2181
This commit is contained in:
Hajime Hoshi 2022-08-16 23:53:34 +09:00
parent 9bd46cb2b5
commit bb2df24f83
2 changed files with 35 additions and 9 deletions

View File

@ -608,6 +608,7 @@ type _DXGI_MODE_SCALING int32
type _DXGI_PRESENT uint32
const (
_DXGI_PRESENT_TEST _DXGI_PRESENT = 0x00000001
_DXGI_PRESENT_ALLOW_TEARING _DXGI_PRESENT = 0x00000200
)
@ -2684,16 +2685,16 @@ func (i *_IDXGISwapChain4) GetCurrentBackBufferIndex() uint32 {
return uint32(r)
}
func (i *_IDXGISwapChain4) Present(syncInterval uint32, flags uint32) error {
func (i *_IDXGISwapChain4) Present(syncInterval uint32, flags uint32) (occluded bool, err error) {
r, _, _ := syscall.Syscall(i.vtbl.Present, 3, uintptr(unsafe.Pointer(i)), uintptr(syncInterval), uintptr(flags))
if uint32(r) != uint32(windows.S_OK) {
// During a screen lock, Present fails (#2179).
if uint32(r) == uint32(windows.DXGI_STATUS_OCCLUDED) {
return nil
return true, nil
}
return fmt.Errorf("directx: IDXGISwapChain4::Present failed: HRESULT(%d)", uint32(r))
return false, fmt.Errorf("directx: IDXGISwapChain4::Present failed: HRESULT(%d)", uint32(r))
}
return nil
return false, nil
}
func (i *_IDXGISwapChain4) ResizeBuffers(bufferCount uint32, width uint32, height uint32, newFormat _DXGI_FORMAT, swapChainFlags uint32) error {

View File

@ -20,6 +20,7 @@ import (
"os"
"reflect"
"strings"
"time"
"unsafe"
"golang.org/x/sys/windows"
@ -150,6 +151,12 @@ type Graphics struct {
vsyncEnabled bool
transparent bool
// occluded reports whether the screen is invisible or not.
occluded bool
// lastTime is the last time for rendering.
lastTime time.Time
pipelineStates
}
@ -825,14 +832,32 @@ func (g *Graphics) presentDesktop() error {
var syncInterval uint32
var flags _DXGI_PRESENT
if g.vsyncEnabled {
syncInterval = 1
} else if g.allowTearing {
flags |= _DXGI_PRESENT_ALLOW_TEARING
if g.occluded {
// The screen is not visible. Test whether we can resume.
flags |= _DXGI_PRESENT_TEST
} else {
// Do actual rendering only when the screen is visible.
if g.vsyncEnabled {
syncInterval = 1
} else if g.allowTearing {
flags |= _DXGI_PRESENT_ALLOW_TEARING
}
}
if err := g.swapChain.Present(syncInterval, uint32(flags)); err != nil {
occluded, err := g.swapChain.Present(syncInterval, uint32(flags))
if err != nil {
return err
}
g.occluded = occluded
// Reduce FPS when the screen is invisible.
now := time.Now()
if g.occluded {
if delta := 100*time.Millisecond - now.Sub(g.lastTime); delta > 0 {
time.Sleep(delta)
}
}
g.lastTime = now
return nil
}