Skip to content

D3D11 gamma support#117

Open
aquadran wants to merge 1 commit into3Shain:mainfrom
WineAndAqua:gamma-suport
Open

D3D11 gamma support#117
aquadran wants to merge 1 commit into3Shain:mainfrom
WineAndAqua:gamma-suport

Conversation

@aquadran
Copy link
Contributor

@aquadran aquadran commented Jan 11, 2026

D3D11 gamma support for game Titanfall 2 and Quantum Break

@aquadran aquadran changed the title WIP dx11 gamma support WIP d3d11 gamma support Jan 11, 2026
@aquadran aquadran changed the title WIP d3d11 gamma support WIP: d3d11 gamma support Jan 11, 2026
@aquadran aquadran force-pushed the gamma-suport branch 12 times, most recently from b1acd91 to 12d626d Compare January 13, 2026 20:01
@aquadran aquadran changed the title WIP: d3d11 gamma support D3D11 gamma support Jan 13, 2026
@aquadran aquadran marked this pull request as ready for review January 13, 2026 20:07
@aquadran aquadran changed the title D3D11 gamma support WIP: D3D11 gamma support Jan 14, 2026
@aquadran aquadran changed the title WIP: D3D11 gamma support D3D11 gamma support Jan 14, 2026
Copy link
Owner

@3Shain 3Shain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to make change for every comment. Mainly it's the design of IMTLDXGIMonitor /IMTLDXGIFactory that needs refactoring.

@3Shain
Copy link
Owner

3Shain commented Feb 2, 2026

Also please split your commit into several smaller chunks.

@aquadran
Copy link
Contributor Author

aquadran commented Feb 2, 2026

Also please split your commit into several smaller chunks.

yes, that was a plan, I wanted to see feedback from you first.

@aquadran
Copy link
Contributor Author

aquadran commented Feb 2, 2026

No need to make change for every comment. Mainly it's the design of IMTLDXGIMonitor /IMTLDXGIFactory that needs refactoring.

I'm not sure what to do. I need to pass gamma curve from MTLDXGIOutput instance which game is using to MTLD3D11SwapChain

@3Shain
Copy link
Owner

3Shain commented Feb 3, 2026

No need to make change for every comment. Mainly it's the design of IMTLDXGIMonitor /IMTLDXGIFactory that needs refactoring.

I'm not sure what to do. I need to pass gamma curve from MTLDXGIOutput instance which game is using to MTLD3D11SwapChain

What's the problem to access the target output in swapchain in fullscreen state? Do you mean it's possible to get multiple instances of MTLDXGIOutput from one logical monitor HMONITOR? Need to check if on Windows it is always the same instance (not only for IDXGIAdapter::EnumOutputs() but also IDXGIFactory::EnumAdapters()). If so, then we need some sort of cache. If not, then we need to hoist the gamma curve state, maybe to adapter in favor of factory (But if factory is more convenient, that's ok, just don't introduce new COM interface => expose public MTLDXGIFactory interface and keep private MTLDXGIFactoryImpl).

@aquadran
Copy link
Contributor Author

aquadran commented Feb 3, 2026

No need to make change for every comment. Mainly it's the design of IMTLDXGIMonitor /IMTLDXGIFactory that needs refactoring.

I'm not sure what to do. I need to pass gamma curve from MTLDXGIOutput instance which game is using to MTLD3D11SwapChain

What's the problem to access the target output in swapchain in fullscreen state? Do you mean it's possible to get multiple instances of MTLDXGIOutput from one logical monitor HMONITOR? Need to check if on Windows it is always the same instance (not only for IDXGIAdapter::EnumOutputs() but also IDXGIFactory::EnumAdapters()). If so, then we need some sort of cache. If not, then we need to hoist the gamma curve state, maybe to adapter in favor of factory (But if factory is more convenient, that's ok, just don't introduce new COM interface => expose public MTLDXGIFactory interface and keep private MTLDXGIFactoryImpl).

what I mean IDXGIAdapter::EnumOutputs() is called few times by engine. So there are few instances. We don't have connection to which one actually is called to update gamma curve. I was wondering of IDXGIAdapter::EnumOutputs(), should it create a new instance every time, but instead keep reference in device and re-use it at next time.

The factory was convenient as it's created along with device. I'll check if using adapter would be fine.

For reference:

CreateDXGIFactory1()
CreateDXGIFactory2()
MTLDXGIFactory::EnumAdapters1()
MTLDXGIAdatper::CreateAdapter()
D3D11CoreCreateDevice()
DeviceImpl::CreateDXMTDevice()
CreateD3D11Device()
MTLDXGIFactory::EnumAdapters1()
MTLDXGIAdatper::CreateAdapter()
MTLDXGIOutput::CreateOutput()
MTLDXGIOutput::MTLDXGIOutput()
MTLDXGIFactory::EnumAdapters1()
MTLDXGIOutput::CreateOutput()
MTLDXGIOutput::MTLDXGIOutput()
DXGIFactory::CreateSwapChain()
DXGIFactory::CreateSwapChainForHwnd()
MTLD3D11DXGIDevice::CreateSwapChain()
MTLDXGIOutput::CreateOutput()
MTLDXGIOutput::MTLDXGIOutput()

3 x Output
2 x Adapter
1 x Factory

@3Shain
Copy link
Owner

3Shain commented Feb 3, 2026

what I mean IDXGIAdapter::EnumOutputs() is called few times by engine. So there are few instances. We don't have connection to which one actually is called to update gamma curve. I was wondering of IDXGIAdapter::EnumOutputs(), should it create a new instance every time, but instead keep reference in device and re-use it at next time.

The factory was convenient as it's created along with device. I'll check if using adapter would be fine.

For reference:

CreateDXGIFactory1()
CreateDXGIFactory2()
MTLDXGIFactory::EnumAdapters1()
MTLDXGIAdatper::CreateAdapter()
D3D11CoreCreateDevice()
DeviceImpl::CreateDXMTDevice()
CreateD3D11Device()
MTLDXGIFactory::EnumAdapters1()
MTLDXGIAdatper::CreateAdapter()
MTLDXGIOutput::CreateOutput()
MTLDXGIOutput::MTLDXGIOutput()
MTLDXGIFactory::EnumAdapters1()
MTLDXGIOutput::CreateOutput()
MTLDXGIOutput::MTLDXGIOutput()
DXGIFactory::CreateSwapChain()
DXGIFactory::CreateSwapChainForHwnd()
MTLD3D11DXGIDevice::CreateSwapChain()
MTLDXGIOutput::CreateOutput()
MTLDXGIOutput::MTLDXGIOutput()

3 x Output 2 x Adapter 1 x Factory

Let's check the Windows behavior. I think there is at least one misalignment because I often get page fault when replying DXMT trace in RenderDoc on Windows 🤔. That worths a separated PR.

@aquadran
Copy link
Contributor Author

aquadran commented Feb 3, 2026

Let's check the Windows behavior. I think there is at least one misalignment because I often get page fault when -replying DXMT trace in RenderDoc on Windows 🤔. That worths a separated PR.

I did api trace under windows, it shows 2 adapters and 1 factory.

When I run api trace with DXMT:
1 adapter and 1 output
plus additional from GetContainingOutput() after switching to fullscreen earlier.

So, last output could store gamma curve. But how to trigger update in swap chain from this output🤔

@3Shain
Copy link
Owner

3Shain commented Feb 4, 2026

Per MSDN:

DXGI’s gamma controls apply only as long as the app is full screen. Windows restores the previous state of the display when the app exits or returns to windowed mode. But Windows doesn’t restore your app’s gamma state if the app re-enters full-screen mode. Your app must explicitly restore its gamma state when it re-enters full-screen mode.

I understand it as SetGammaControl() has no effect if the output is not the fullscreen target. So there is another thing to verify on Windows: is it valid to get arbitrary instance of IDXGIOutput, set gamma curve, use another different instance of IDXGIOutput (of same HMONITOR) for entering fullscreen, and previous gamma curve setting is still applied.

@aquadran
Copy link
Contributor Author

aquadran commented Feb 4, 2026

another instance from api trace log return some error, and get dropped.

Per MSDN:

DXGI’s gamma controls apply only as long as the app is full screen. Windows restores the previous state of the display when the app exits or returns to windowed mode. But Windows doesn’t restore your app’s gamma state if the app re-enters full-screen mode. Your app must explicitly restore its gamma state when it re-enters full-screen mode.

I understand it as SetGammaControl() has no effect if the output is not the fullscreen target. So there is another thing to verify on Windows: is it valid to get arbitrary instance of IDXGIOutput, set gamma curve, use another different instance of IDXGIOutput (of same HMONITOR) for entering fullscreen, and previous gamma curve setting is still applied.

I did check how entering and leaving fullscreen involve Output with DXMT.
Before each enter fullscreen, the Output is enumerated (new instance) from Adapter and based on new instance a gamma ramp is set. There is nothing going on while leaving fullscreen.
On Windows it looks similar.

I also checked what happened with instances on Windows.
Game engine enumerate adapters first. There are 2 adapters - HW GPU and SW renderer.
It enumerate by index until reach not found. For each adapter it enumerate Outputs connected to adapter until not found. For SW renderer it does not found any Output.

It's hard to figure out what is going on from api traces. Like to check if IDXGIOutput instances are connected, or old ones are invalided (my guess is yes).
About old gamma curve set on other instances. The engine set gamma each time switching to fullscreen.

@aquadran aquadran changed the title D3D11 gamma support WIP: D3D11 gamma support Feb 10, 2026
@aquadran aquadran force-pushed the gamma-suport branch 3 times, most recently from 78c2126 to 7ea3dd5 Compare February 12, 2026 12:45
@aquadran aquadran changed the title WIP: D3D11 gamma support D3D11 gamma support Feb 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants