From f5679211cd188845b894b1e0bd1ed627bbf50b47 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 12 Apr 2018 13:38:22 +0200 Subject: [PATCH] [dxgi] Re-implement gamma control functions --- src/dxgi/dxgi_adapter.cpp | 43 ++++++++++++--- src/dxgi/dxgi_adapter.h | 41 +++++++++----- src/dxgi/dxgi_output.cpp | 66 ++++++++++++++++++----- src/dxgi/dxgi_output.h | 16 ++++-- src/dxgi/dxgi_swapchain.cpp | 19 ++++++- src/dxgi/dxgi_swapchain.h | 1 + src/dxgi/shaders/dxgi_presenter_frag.frag | 44 +++++++-------- 7 files changed, 169 insertions(+), 61 deletions(-) diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp index ab659add..70924348 100644 --- a/src/dxgi/dxgi_adapter.cpp +++ b/src/dxgi/dxgi_adapter.cpp @@ -49,16 +49,16 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DxgiAdapter::CheckInterfaceSupport( - REFGUID InterfaceName, - LARGE_INTEGER *pUMDVersion) { + REFGUID InterfaceName, + LARGE_INTEGER* pUMDVersion) { Logger::err("DxgiAdapter::CheckInterfaceSupport: No D3D10 support"); return DXGI_ERROR_UNSUPPORTED; } HRESULT STDMETHODCALLTYPE DxgiAdapter::EnumOutputs( - UINT Output, - IDXGIOutput **ppOutput) { + UINT Output, + IDXGIOutput** ppOutput) { InitReturnPtr(ppOutput); if (ppOutput == nullptr) @@ -173,7 +173,9 @@ namespace dxvk { } - DxgiFormatInfo STDMETHODCALLTYPE DxgiAdapter::LookupFormat(DXGI_FORMAT format, DxgiFormatMode mode) { + DxgiFormatInfo STDMETHODCALLTYPE DxgiAdapter::LookupFormat( + DXGI_FORMAT format, + DxgiFormatMode mode) { // If the mode is 'Any', probe color formats first if (mode != DxgiFormatMode::Depth) { auto color = m_colorFormats.find(format); @@ -194,8 +196,8 @@ namespace dxvk { HRESULT DxgiAdapter::GetOutputFromMonitor( - HMONITOR Monitor, - IDXGIOutput** ppOutput) { + HMONITOR Monitor, + IDXGIOutput** ppOutput) { if (ppOutput == nullptr) return DXGI_ERROR_INVALID_CALL; @@ -215,6 +217,33 @@ namespace dxvk { } + HRESULT DxgiAdapter::GetOutputData( + HMONITOR Monitor, + DXGI_VK_OUTPUT_DATA* pOutputData) { + std::lock_guard lock(m_outputMutex); + + auto entry = m_outputData.find(Monitor); + if (entry == m_outputData.end()) + return DXGI_ERROR_NOT_FOUND; + + if (pOutputData == nullptr) + return S_FALSE; + + *pOutputData = entry->second; + return S_OK; + } + + + HRESULT DxgiAdapter::SetOutputData( + HMONITOR Monitor, + const DXGI_VK_OUTPUT_DATA* pOutputData) { + std::lock_guard lock(m_outputMutex); + + m_outputData.insert_or_assign(Monitor, *pOutputData); + return S_OK; + } + + void DxgiAdapter::AddColorFormatTypeless( DXGI_FORMAT srcFormat, VkFormat dstFormat) { diff --git a/src/dxgi/dxgi_adapter.h b/src/dxgi/dxgi_adapter.h index 76f4ecaa..1c7d9f1b 100644 --- a/src/dxgi/dxgi_adapter.h +++ b/src/dxgi/dxgi_adapter.h @@ -9,7 +9,7 @@ #include #include "dxgi_interfaces.h" -#include "dxgi_object.h" +#include "dxgi_output.h" namespace dxvk { @@ -26,26 +26,26 @@ namespace dxvk { ~DxgiAdapter(); HRESULT STDMETHODCALLTYPE QueryInterface( - REFIID riid, - void **ppvObject) final; + REFIID riid, + void** ppvObject) final; HRESULT STDMETHODCALLTYPE GetParent( - REFIID riid, - void **ppParent) final; + REFIID riid, + void** ppParent) final; HRESULT STDMETHODCALLTYPE CheckInterfaceSupport( - REFGUID InterfaceName, - LARGE_INTEGER *pUMDVersion) final; + REFGUID InterfaceName, + LARGE_INTEGER* pUMDVersion) final; HRESULT STDMETHODCALLTYPE EnumOutputs( - UINT Output, - IDXGIOutput **ppOutput) final; + UINT Output, + IDXGIOutput** ppOutput) final; HRESULT STDMETHODCALLTYPE GetDesc( - DXGI_ADAPTER_DESC *pDesc) final; + DXGI_ADAPTER_DESC* pDesc) final; HRESULT STDMETHODCALLTYPE GetDesc1( - DXGI_ADAPTER_DESC1 *pDesc) final; + DXGI_ADAPTER_DESC1* pDesc) final; Rc STDMETHODCALLTYPE GetDXVKAdapter() final; @@ -55,15 +55,25 @@ namespace dxvk { IDXGIVkDevice** ppDevice) final; DxgiFormatInfo STDMETHODCALLTYPE LookupFormat( - DXGI_FORMAT format, DxgiFormatMode mode) final; + DXGI_FORMAT format, + DxgiFormatMode mode) final; HRESULT GetOutputFromMonitor( - HMONITOR Monitor, - IDXGIOutput** ppOutput); + HMONITOR Monitor, + IDXGIOutput** ppOutput); + + HRESULT GetOutputData( + HMONITOR Monitor, + DXGI_VK_OUTPUT_DATA* pOutputData); + + HRESULT SetOutputData( + HMONITOR Monitor, + const DXGI_VK_OUTPUT_DATA* pOutputData); private: using FormatMap = std::unordered_map; + using OutputMap = std::unordered_map; Com m_factory; Rc m_adapter; @@ -71,6 +81,9 @@ namespace dxvk { FormatMap m_colorFormats; FormatMap m_depthFormats; + std::mutex m_outputMutex; + OutputMap m_outputData; + void AddColorFormatTypeless( DXGI_FORMAT srcFormat, VkFormat dstFormat); diff --git a/src/dxgi/dxgi_output.cpp b/src/dxgi/dxgi_output.cpp index dd4206a5..7ac38b89 100644 --- a/src/dxgi/dxgi_output.cpp +++ b/src/dxgi/dxgi_output.cpp @@ -17,7 +17,21 @@ namespace dxvk { HMONITOR monitor) : m_adapter(adapter), m_monitor(monitor) { - + // Init output data if necessary + if (FAILED(m_adapter->GetOutputData(m_monitor, nullptr))) { + DXGI_VK_OUTPUT_DATA outputData; + outputData.FrameStats = DXGI_FRAME_STATISTICS(); + outputData.GammaCurve.Scale = { 1.0f, 1.0f, 1.0f }; + outputData.GammaCurve.Offset = { 0.0f, 0.0f, 0.0f }; + + for (uint32_t i = 0; i < DxgiPresenterGammaRamp::CpCount; i++) { + const float value = DxgiPresenterGammaRamp::cpLocation(i); + outputData.GammaCurve.GammaCurve[i] = { value, value, value }; + } + + outputData.GammaDirty = FALSE; + m_adapter->SetOutputData(m_monitor, &outputData); + } } @@ -223,25 +237,39 @@ namespace dxvk { } - HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData(IDXGISurface *pDestination) { + HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData(IDXGISurface* pDestination) { Logger::err("DxgiOutput::GetDisplaySurfaceData: Not implemented"); return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE DxgiOutput::GetFrameStatistics(DXGI_FRAME_STATISTICS *pStats) { - Logger::err("DxgiOutput::GetFrameStatistics: Not implemented"); - return E_NOTIMPL; + HRESULT STDMETHODCALLTYPE DxgiOutput::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) { + DXGI_VK_OUTPUT_DATA outputData; + + if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) { + Logger::err("DxgiOutput: Failed to query output data"); + return E_FAIL; + } + + *pStats = outputData.FrameStats; + return S_OK; } - HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL *pArray) { - Logger::err("DxgiOutput::GetGammaControl: Not implemented"); - return E_NOTIMPL; + HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL* pArray) { + DXGI_VK_OUTPUT_DATA outputData; + + if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) { + Logger::err("DxgiOutput: Failed to query output data"); + return E_FAIL; + } + + *pArray = outputData.GammaCurve; + return S_OK; } - HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControlCapabilities(DXGI_GAMMA_CONTROL_CAPABILITIES *pGammaCaps) { + HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControlCapabilities(DXGI_GAMMA_CONTROL_CAPABILITIES* pGammaCaps) { pGammaCaps->ScaleAndOffsetSupported = TRUE; pGammaCaps->MaxConvertedValue = 1.0f; pGammaCaps->MinConvertedValue = 0.0f; @@ -258,15 +286,27 @@ namespace dxvk { } - HRESULT STDMETHODCALLTYPE DxgiOutput::SetDisplaySurface(IDXGISurface *pScanoutSurface) { + HRESULT STDMETHODCALLTYPE DxgiOutput::SetDisplaySurface(IDXGISurface* pScanoutSurface) { Logger::err("DxgiOutput::SetDisplaySurface: Not implemented"); return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE DxgiOutput::SetGammaControl(const DXGI_GAMMA_CONTROL *pArray) { - Logger::err("DxgiOutput::SetGammaControl: Not implemented"); - return E_NOTIMPL; + HRESULT STDMETHODCALLTYPE DxgiOutput::SetGammaControl(const DXGI_GAMMA_CONTROL* pArray) { + DXGI_VK_OUTPUT_DATA outputData; + + if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) { + Logger::err("DxgiOutput: Failed to query output data"); + return E_FAIL; + } + + outputData.GammaCurve = *pArray; + outputData.GammaDirty = TRUE; + + if (FAILED(m_adapter->SetOutputData(m_monitor, &outputData))) { + Logger::err("DxgiOutput: Failed to query output data"); + return E_FAIL; + } return S_OK; } diff --git a/src/dxgi/dxgi_output.h b/src/dxgi/dxgi_output.h index 3543cae1..e012d4b0 100644 --- a/src/dxgi/dxgi_output.h +++ b/src/dxgi/dxgi_output.h @@ -1,13 +1,23 @@ #pragma once -#include - #include "dxgi_object.h" namespace dxvk { class DxgiAdapter; - class DxgiSwapChain; + + /** + * \brief Per-output data + * + * Persistent data for a single output, which + * is addressed using the \c HMONITOR handle. + */ + struct DXGI_VK_OUTPUT_DATA { + DXGI_FRAME_STATISTICS FrameStats; + DXGI_GAMMA_CONTROL GammaCurve; + BOOL GammaDirty; + }; + class DxgiOutput : public DxgiObject { diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index 2f4be95b..d7a1f3bf 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -10,7 +10,8 @@ namespace dxvk { IUnknown* pDevice, DXGI_SWAP_CHAIN_DESC* pDesc) : m_factory (factory), - m_desc (*pDesc) { + m_desc (*pDesc), + m_monitor (nullptr) { // Retrieve a device pointer that allows us to // communicate with the underlying D3D device @@ -177,6 +178,17 @@ namespace dxvk { return S_OK; try { + // If in fullscreen mode, apply any updated gamma curve + // if it has been changed since the last present call. + DXGI_VK_OUTPUT_DATA outputData; + + if (SUCCEEDED(m_adapter->GetOutputData(m_monitor, &outputData)) && outputData.GammaDirty) { + SetGammaControl(&outputData.GammaCurve); + + outputData.GammaDirty = FALSE; + m_adapter->SetOutputData(m_monitor, &outputData); + } + // Submit pending rendering commands // before recording the present code. m_presentDevice->FlushRenderingCommands(); @@ -425,12 +437,15 @@ namespace dxvk { ::SetWindowPos(m_desc.OutputWindow, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE); + + m_monitor = desc.Monitor; return S_OK; } HRESULT DxgiSwapChain::LeaveFullscreenMode() { m_desc.Windowed = TRUE; + m_monitor = nullptr; // FIXME wine only restores window flags if the application // has not modified them in the meantime. Some applications @@ -444,7 +459,7 @@ namespace dxvk { rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE); - return S_OK; + return SetDefaultGammaControl(); } diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index db38d27d..19f26d17 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -110,6 +110,7 @@ namespace dxvk { Rc m_presenter; Com m_backBuffer; + HMONITOR m_monitor; WindowState m_windowState; DxgiPresenterGammaRamp m_gammaControl; diff --git a/src/dxgi/shaders/dxgi_presenter_frag.frag b/src/dxgi/shaders/dxgi_presenter_frag.frag index ae8fa251..49859467 100644 --- a/src/dxgi/shaders/dxgi_presenter_frag.frag +++ b/src/dxgi/shaders/dxgi_presenter_frag.frag @@ -18,26 +18,26 @@ layout(location = 0) out vec4 o_color; void main() { o_color = texture(sampler2D(t_texture, s_sampler), i_texcoord); -// vec3 cp_lookup = o_color.rgb; -// cp_lookup *= u_gamma_ramp.in_factor.rgb; -// cp_lookup += u_gamma_ramp.in_offset.rgb; -// -// cp_lookup = clamp( -// cp_lookup * float(CP_COUNT - 1), -// 0.0f, float(CP_COUNT - 1)); -// -// vec3 cp_fpart = fract(cp_lookup); -// ivec3 cp_index = ivec3(cp_lookup); -// -// for (int i = 0; i < 3; i++) { -// int cp_entry = cp_index[i]; -// -// float lo = u_gamma_ramp.cp_values[cp_entry + 0][i]; -// float hi = u_gamma_ramp.cp_values[cp_entry + 1][i]; -// -// if (cp_entry == CP_COUNT - 1) -// hi = lo; -// -// o_color[i] = mix(lo, hi, cp_fpart[i]); -// } + vec3 cp_lookup = o_color.rgb; + cp_lookup *= u_gamma_ramp.in_factor.rgb; + cp_lookup += u_gamma_ramp.in_offset.rgb; + + cp_lookup = clamp( + cp_lookup * float(CP_COUNT - 1), + 0.0f, float(CP_COUNT - 1)); + + vec3 cp_fpart = fract(cp_lookup); + ivec3 cp_index = ivec3(cp_lookup); + + for (int i = 0; i < 3; i++) { + int cp_entry = cp_index[i]; + + float lo = u_gamma_ramp.cp_values[cp_entry + 0][i]; + float hi = u_gamma_ramp.cp_values[cp_entry + 1][i]; + + if (cp_entry == CP_COUNT - 1) + hi = lo; + + o_color[i] = mix(lo, hi, cp_fpart[i]); + } } \ No newline at end of file