From 0035ba0bd1a35c4a27c2933af17276af7b7b7e1d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 10 Jul 2022 01:17:33 +0900 Subject: [PATCH] internal/graphicsdriver/directx: allow tearing when vsync is off Updates #2034 Updates #2188 --- .../graphicsdriver/directx/api_windows.go | 81 +++++++++++++++++++ .../directx/graphics_windows.go | 24 +++++- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/internal/graphicsdriver/directx/api_windows.go b/internal/graphicsdriver/directx/api_windows.go index 23897e695..779a579ae 100644 --- a/internal/graphicsdriver/directx/api_windows.go +++ b/internal/graphicsdriver/directx/api_windows.go @@ -577,6 +577,12 @@ const ( type _DXGI_COLOR_SPACE_TYPE int32 +type _DXGI_FEATURE int32 + +const ( + _DXGI_FEATURE_PRESENT_ALLOW_TEARING _DXGI_FEATURE = 0 +) + type _DXGI_FORMAT int32 const ( @@ -593,8 +599,20 @@ type _DXGI_MODE_SCANLINE_ORDER int32 type _DXGI_MODE_SCALING int32 +type _DXGI_PRESENT uint32 + +const ( + _DXGI_PRESENT_ALLOW_TEARING _DXGI_PRESENT = 0x00000200 +) + type _DXGI_SCALING int32 +type _DXGI_SWAP_CHAIN_FLAG int32 + +const ( + _DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING _DXGI_SWAP_CHAIN_FLAG = 2048 +) + type _DXGI_SWAP_EFFECT int32 const ( @@ -634,6 +652,7 @@ var ( _IID_IDXGIAdapter1 = windows.GUID{Data1: 0x29038f61, Data2: 0x3839, Data3: 0x4626, Data4: [...]byte{0x91, 0xfd, 0x08, 0x68, 0x79, 0x01, 0x1a, 0x05}} _IID_IDXGIDevice = windows.GUID{Data1: 0x54ec77fa, Data2: 0x1377, Data3: 0x44e6, Data4: [...]byte{0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c}} _IID_IDXGIFactory4 = windows.GUID{Data1: 0x1bc6ea02, Data2: 0xef36, Data3: 0x464f, Data4: [...]byte{0xbf, 0x0c, 0x21, 0xca, 0x39, 0xe5, 0x16, 0x8a}} + _IID_IDXGIFactory5 = windows.GUID{Data1: 0x7632e1f5, Data2: 0xee65, Data3: 0x4dca, Data4: [...]byte{0x87, 0xfd, 0x84, 0xcd, 0x75, 0xf8, 0x83, 0x8d}} ) type _D3D12_BLEND_DESC struct { @@ -2429,10 +2448,72 @@ func (i *_IDXGIFactory4) MakeWindowAssociation(windowHandle windows.HWND, flags return nil } +func (i *_IDXGIFactory4) QueryInterface(riid *windows.GUID, ppvObject *unsafe.Pointer) error { + r, _, _ := syscall.Syscall(i.vtbl.QueryInterface, 3, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(riid)), uintptr(unsafe.Pointer(ppvObject))) + runtime.KeepAlive(riid) + runtime.KeepAlive(ppvObject) + if uint32(r) != uint32(windows.S_OK) { + return fmt.Errorf("directx: IDXGIFactory4::QueryInterface failed: HRESULT(%d)", uint32(r)) + } + return nil +} + func (i *_IDXGIFactory4) Release() { syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0) } +type _IDXGIFactory5 struct { + vtbl *_IDXGIFactory5_Vtbl +} + +type _IDXGIFactory5_Vtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr + + SetPrivateData uintptr + SetPrivateDataInterface uintptr + GetPrivateData uintptr + GetParent uintptr + EnumAdapters uintptr + MakeWindowAssociation uintptr + GetWindowAssociation uintptr + CreateSwapChain uintptr + CreateSoftwareAdapter uintptr + EnumAdapters1 uintptr + IsCurrent uintptr + IsWindowedStereoEnabled uintptr + CreateSwapChainForHwnd uintptr + CreateSwapChainForCoreWindow uintptr + GetSharedResourceAdapterLuid uintptr + RegisterStereoStatusWindow uintptr + RegisterStereoStatusEvent uintptr + UnregisterStereoStatus uintptr + RegisterOcclusionStatusWindow uintptr + RegisterOcclusionStatusEvent uintptr + UnregisterOcclusionStatus uintptr + CreateSwapChainForComposition uintptr + GetCreationFlags uintptr + EnumAdapterByLuid uintptr + EnumWarpAdapter uintptr + CheckFeatureSupport uintptr +} + +func (i *_IDXGIFactory5) CheckFeatureSupport(feature _DXGI_FEATURE, pFeatureSupportData unsafe.Pointer, featureSupportDataSize uint32) error { + r, _, _ := syscall.Syscall6(i.vtbl.CheckFeatureSupport, 4, uintptr(unsafe.Pointer(i)), + uintptr(feature), uintptr(pFeatureSupportData), uintptr(featureSupportDataSize), + 0, 0) + runtime.KeepAlive(pFeatureSupportData) + if uint32(r) != uint32(windows.S_OK) { + return fmt.Errorf("directx: IDXGIFactory5::CheckFeatureSupport failed: HRESULT(%d)", uint32(r)) + } + return nil +} + +func (i *_IDXGIFactory5) Release() { + syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0) +} + type _IDXGIOutput struct { vtbl *_IDXGIOutput_Vtbl } diff --git a/internal/graphicsdriver/directx/graphics_windows.go b/internal/graphicsdriver/directx/graphics_windows.go index c1900b166..3987f0cec 100644 --- a/internal/graphicsdriver/directx/graphics_windows.go +++ b/internal/graphicsdriver/directx/graphics_windows.go @@ -103,6 +103,8 @@ type Graphics struct { fenceValues [frameCount]uint64 fenceWaitEvent windows.Handle + allowTearing bool + // drawCommandAllocators are command allocators for a 3D engine (DrawIndexedInstanced). // For the word 'engine', see https://docs.microsoft.com/en-us/windows/win32/direct3d12/user-mode-heap-synchronization. // The term 'draw' is used instead of '3D' in this package. @@ -268,6 +270,15 @@ func (g *Graphics) initializeDesktop(useWARP bool, useDebugLayer bool) (ferr err return err } + var factory *_IDXGIFactory5 + if err := g.factory.QueryInterface(&_IID_IDXGIFactory5, (*unsafe.Pointer)(unsafe.Pointer(&factory))); err == nil && factory != nil { + defer factory.Release() + var allowTearing int32 + if err := factory.CheckFeatureSupport(_DXGI_FEATURE_PRESENT_ALLOW_TEARING, unsafe.Pointer(&allowTearing), uint32(unsafe.Sizeof(allowTearing))); err == nil && allowTearing != 0 { + g.allowTearing = true + } + } + if err := g.initializeMembers(); err != nil { return err } @@ -524,7 +535,7 @@ func (g *Graphics) initSwapChainDesktop(width, height int) (ferr error) { // // IDXGIFactory::CreateSwapChain: Alpha blended swapchains must be created with CreateSwapChainForComposition, // or CreateSwapChainForCoreWindow with the DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER flag - s, err := g.factory.CreateSwapChainForHwnd(unsafe.Pointer(g.commandQueue), g.window, &_DXGI_SWAP_CHAIN_DESC1{ + desc := &_DXGI_SWAP_CHAIN_DESC1{ Width: uint32(width), Height: uint32(height), Format: _DXGI_FORMAT_B8G8R8A8_UNORM, @@ -535,7 +546,11 @@ func (g *Graphics) initSwapChainDesktop(width, height int) (ferr error) { Count: 1, Quality: 0, }, - }, nil, nil) + } + if g.allowTearing { + desc.Flags |= uint32(_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) + } + s, err := g.factory.CreateSwapChainForHwnd(unsafe.Pointer(g.commandQueue), g.window, desc, nil, nil) if err != nil { return err } @@ -781,10 +796,13 @@ 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 err := g.swapChain.Present(syncInterval, 0); err != nil { + if err := g.swapChain.Present(syncInterval, uint32(flags)); err != nil { return err }