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 type _DXGI_PRESENT uint32
const ( const (
_DXGI_PRESENT_TEST _DXGI_PRESENT = 0x00000001
_DXGI_PRESENT_ALLOW_TEARING _DXGI_PRESENT = 0x00000200 _DXGI_PRESENT_ALLOW_TEARING _DXGI_PRESENT = 0x00000200
) )
@ -2684,16 +2685,16 @@ func (i *_IDXGISwapChain4) GetCurrentBackBufferIndex() uint32 {
return uint32(r) 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)) r, _, _ := syscall.Syscall(i.vtbl.Present, 3, uintptr(unsafe.Pointer(i)), uintptr(syncInterval), uintptr(flags))
if uint32(r) != uint32(windows.S_OK) { if uint32(r) != uint32(windows.S_OK) {
// During a screen lock, Present fails (#2179). // During a screen lock, Present fails (#2179).
if uint32(r) == uint32(windows.DXGI_STATUS_OCCLUDED) { 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 { func (i *_IDXGISwapChain4) ResizeBuffers(bufferCount uint32, width uint32, height uint32, newFormat _DXGI_FORMAT, swapChainFlags uint32) error {

View File

@ -20,6 +20,7 @@ import (
"os" "os"
"reflect" "reflect"
"strings" "strings"
"time"
"unsafe" "unsafe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
@ -150,6 +151,12 @@ type Graphics struct {
vsyncEnabled bool vsyncEnabled bool
transparent 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 pipelineStates
} }
@ -825,14 +832,32 @@ func (g *Graphics) presentDesktop() error {
var syncInterval uint32 var syncInterval uint32
var flags _DXGI_PRESENT var flags _DXGI_PRESENT
if g.vsyncEnabled { if g.occluded {
syncInterval = 1 // The screen is not visible. Test whether we can resume.
} else if g.allowTearing { flags |= _DXGI_PRESENT_TEST
flags |= _DXGI_PRESENT_ALLOW_TEARING } 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 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 return nil
} }