From 6f41a05264ffdb84f50cf42f90e2c0e613eb0f06 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 18 Sep 2023 03:26:49 +0900 Subject: [PATCH] Revert "internal/graphicsdriver/directx: remove tearing" This reverts commit 78d3e4273b0daee8f82dc20debf5fa4e68da5955. Reason: tearing is potentially risky but needed for maximizing FPS. Also, tearing is officially recommended by Microsoft (see [1]) Closes #2697 [1] https://github.com/microsoft/DirectX-Graphics-Samples/blob/master/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.cpp#L520 --- .../graphicsdriver/directx/dxgi_windows.go | 69 ++++++++++++++++++- .../directx/graphics_windows.go | 24 ++++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/internal/graphicsdriver/directx/dxgi_windows.go b/internal/graphicsdriver/directx/dxgi_windows.go index 63e8264e0..69b9e9300 100644 --- a/internal/graphicsdriver/directx/dxgi_windows.go +++ b/internal/graphicsdriver/directx/dxgi_windows.go @@ -35,6 +35,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 ( @@ -54,11 +60,18 @@ type _DXGI_MODE_SCALING int32 type _DXGI_PRESENT uint32 const ( - _DXGI_PRESENT_TEST _DXGI_PRESENT = 0x00000001 + _DXGI_PRESENT_TEST _DXGI_PRESENT = 0x00000001 + _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 ( @@ -90,6 +103,7 @@ var ( _IID_IDXGIDevice = windows.GUID{Data1: 0x54ec77fa, Data2: 0x1377, Data3: 0x44e6, Data4: [...]byte{0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c}} _IID_IDXGIFactory = windows.GUID{Data1: 0x7b7166ec, Data2: 0x21c7, Data3: 0x44ae, Data4: [...]byte{0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69}} _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}} _IID_IDXGISwapChain4 = windows.GUID{Data1: 0x3d585d5a, Data2: 0xbd4a, Data3: 0x489e, Data4: [...]byte{0xb1, 0xf4, 0x3d, 0xbc, 0xb6, 0x45, 0x2f, 0xfb}} ) @@ -387,6 +401,59 @@ func (i *_IDXGIFactory4) Release() uint32 { return uint32(r) } +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: %w", handleError(windows.Handle(uint32(r)))) + } + return nil +} + +func (i *_IDXGIFactory5) Release() uint32 { + r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0) + return uint32(r) +} + type _IDXGIOutput struct { vtbl *_IDXGIOutput_Vtbl } diff --git a/internal/graphicsdriver/directx/graphics_windows.go b/internal/graphicsdriver/directx/graphics_windows.go index 80d1c0e94..a6d56571b 100644 --- a/internal/graphicsdriver/directx/graphics_windows.go +++ b/internal/graphicsdriver/directx/graphics_windows.go @@ -150,6 +150,8 @@ type graphicsInfra struct { swapChain *_IDXGISwapChain swapChain4 *_IDXGISwapChain4 + allowTearing bool + // occluded reports whether the screen is invisible or not. occluded bool @@ -166,6 +168,16 @@ func newGraphicsInfra(factory *_IDXGIFactory) (*graphicsInfra, error) { } runtime.SetFinalizer(g, (*graphicsInfra).release) + if f, err := g.factory.QueryInterface(&_IID_IDXGIFactory5); err == nil && f != nil { + factory := (*_IDXGIFactory5)(f) + 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 + } + } + return g, nil } @@ -266,6 +278,10 @@ func (g *graphicsInfra) initSwapChain(width, height int, device unsafe.Pointer, } g.bufferCount = int(desc.BufferCount) + + if g.allowTearing { + desc.Flags |= uint32(_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) + } s, err := g.factory.CreateSwapChain(device, desc) if err != nil { return err @@ -295,7 +311,11 @@ func (g *graphicsInfra) resizeSwapChain(width, height int) error { return fmt.Errorf("directx: swap chain must be initialized at resizeSwapChain, but is not") } - if err := g.swapChain.ResizeBuffers(uint32(g.bufferCount), uint32(width), uint32(height), _DXGI_FORMAT_B8G8R8A8_UNORM, 0); err != nil { + var flag uint32 + if g.allowTearing { + flag |= uint32(_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) + } + if err := g.swapChain.ResizeBuffers(uint32(g.bufferCount), uint32(width), uint32(height), _DXGI_FORMAT_B8G8R8A8_UNORM, flag); err != nil { return err } return nil @@ -322,6 +342,8 @@ func (g *graphicsInfra) present(vsyncEnabled bool) error { // Do actual rendering only when the screen is visible. if vsyncEnabled { syncInterval = 1 + } else if g.allowTearing { + flags |= _DXGI_PRESENT_ALLOW_TEARING } }