From b16e6b62b4c8aa59f7f0cc22b8f9d7a3a4a07b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Ko=C5=82odziejski?= Date: Mon, 22 Dec 2025 22:43:03 +0100 Subject: [PATCH] feat(d3d11, dxmt): gamma support --- src/d3d11/d3d11_swapchain.cpp | 16 ++++++++- src/dxgi/dxgi_adapter.cpp | 11 +++--- src/dxgi/dxgi_factory.cpp | 19 ++++++++-- src/dxgi/dxgi_interfaces.h | 17 +++++++++ src/dxgi/dxgi_monitor.cpp | 50 ++++++++++++++++++++++++++ src/dxgi/dxgi_monitor.hpp | 28 +++++++++++++++ src/dxgi/dxgi_output.cpp | 68 ++++++++++++++++++++++++++++++----- src/dxgi/meson.build | 1 + src/dxmt/dxmt_command.metal | 10 ++++++ src/dxmt/dxmt_presenter.cpp | 48 +++++++++++++++++++++++-- src/dxmt/dxmt_presenter.hpp | 17 ++++++++- 11 files changed, 263 insertions(+), 22 deletions(-) create mode 100644 src/dxgi/dxgi_monitor.cpp create mode 100644 src/dxgi/dxgi_monitor.hpp diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index 55d3cade..80443514 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -20,6 +20,7 @@ #include "wsi_window.hpp" #include "dxmt_info.hpp" #include "dxmt_presenter.hpp" +#include "dxgi_monitor.hpp" #include #include @@ -92,9 +93,20 @@ class MTLD3D11SwapChain final : public MTLDXGISubObject("d3d11.metalSpatialUpscaleFactor", 2), 1.0f); } + DXMTGammaCurve *gamma_curve = nullptr; + Com monitor_info; + if (FAILED(factory_->QueryInterface(IID_PPV_ARGS(&monitor_info)))) { + ERR("MTLD3D11SwapChain: failed to get IMTLDXGIMonitor"); + } else { + MTLDXGI_MONITOR_DATA *monitor_data; + if (monitor_info->GetMonitorData(&monitor_data) == S_OK) + gamma_curve = &monitor_data->gammaCurve; + } + presenter = Rc(new Presenter(pDevice->GetMTLDevice(), layer_weak_, pDevice->GetDXMTDevice().queue().cmd_library, - scale_factor, desc_.SampleDesc.Count)); + scale_factor, desc_.SampleDesc.Count, + gamma_curve)); frame_latency = kSwapchainLatency; present_semaphore_ = CreateSemaphore(nullptr, frame_latency, @@ -596,6 +608,8 @@ class MTLD3D11SwapChain final : public MTLDXGISubObject lock(device_->mutex); + presenter->updateGammaLUT(); + device_context_->PrepareFlush(); auto &cmd_queue = device_->GetDXMTDevice().queue(); auto chunk = cmd_queue.CurrentChunk(); diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp index 6af6c64d..6ec8ab40 100644 --- a/src/dxgi/dxgi_adapter.cpp +++ b/src/dxgi/dxgi_adapter.cpp @@ -11,7 +11,7 @@ namespace dxmt { -Com CreateOutput(IMTLDXGIAdapter *pAadapter, HMONITOR monitor, DxgiOptions &options); +Com CreateOutput(IMTLDXGIAdapter *pAadapter, IMTLDXGIFactory *pFactory, HMONITOR monitor, DxgiOptions &options); LUID GetAdapterLuid(WMT::Device device) { // NOTE: use big-endian registryID, be consistent with MVK @@ -20,7 +20,7 @@ LUID GetAdapterLuid(WMT::Device device) { class MTLDXGIAdatper : public MTLDXGIObject { public: - MTLDXGIAdatper(WMT::Device device, IDXGIFactory *factory, Config &config) + MTLDXGIAdatper(WMT::Device device, IMTLDXGIFactory *factory, Config &config) : device_(device), factory_(factory), options_(config) { D3DKMT_OPENADAPTERFROMLUID open = {}; open.AdapterLuid = GetAdapterLuid(device_); @@ -190,9 +190,10 @@ class MTLDXGIAdatper : public MTLDXGIObject { if (monitor == nullptr) return DXGI_ERROR_NOT_FOUND; - *ppOutput = CreateOutput(this, monitor, options_); + *ppOutput = CreateOutput(this, factory_.ptr(), monitor, options_); return S_OK; } + HRESULT STDMETHODCALLTYPE CheckInterfaceSupport(const GUID &guid, LARGE_INTEGER *umd_version) final { HRESULT hr = DXGI_ERROR_UNSUPPORTED; @@ -274,13 +275,13 @@ class MTLDXGIAdatper : public MTLDXGIObject { private: WMT::Reference device_; D3DKMT_HANDLE local_kmt_ = 0; - Com factory_; + Com factory_; DxgiOptions options_; uint64_t mem_reserved_[2] = {0, 0}; }; Com CreateAdapter(WMT::Device Device, - IDXGIFactory2 *pFactory, Config &config) { + IMTLDXGIFactory *pFactory, Config &config) { return Com::transfer( new MTLDXGIAdatper(Device, pFactory, config)); } diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp index 0b3fa097..a3c7a46a 100644 --- a/src/dxgi/dxgi_factory.cpp +++ b/src/dxgi/dxgi_factory.cpp @@ -1,6 +1,7 @@ #include "com/com_pointer.hpp" #include "config/config.hpp" #include "dxgi_interfaces.h" +#include "dxgi_monitor.hpp" #include "dxgi_object.hpp" #include "com/com_guid.hpp" #include "log/log.hpp" @@ -11,12 +12,12 @@ namespace dxmt { Com CreateAdapter(WMT::Device Device, - IDXGIFactory2 *pFactory, Config &config); + IMTLDXGIFactory *pFactory, Config &config); -class MTLDXGIFactory : public MTLDXGIObject { +class MTLDXGIFactory : public MTLDXGIObject { public: - MTLDXGIFactory(UINT Flags) : flags_(Flags) {}; + MTLDXGIFactory(UINT Flags) : flags_(Flags), monitor_info_(new MTLDXGIMonitor(this)) {}; HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) final { @@ -34,6 +35,11 @@ class MTLDXGIFactory : public MTLDXGIObject { return S_OK; } + if (riid == __uuidof(IMTLDXGIMonitor)) { + *ppvObject = ref(monitor_info_); + return S_OK; + } + if (logQueryInterfaceError(__uuidof(IDXGIFactory2), riid)) { WARN("DXGIFactory: Unknown interface query ", str::format(riid)); } @@ -301,10 +307,17 @@ class MTLDXGIFactory : public MTLDXGIObject { return adapter->QueryInterface(riid, ppvAdapter); }; + MTLDXGIMonitor STDMETHODCALLTYPE + *GetMonitor() override { + return monitor_info_; + } + private: UINT flags_; HWND associated_window_ = nullptr; + + MTLDXGIMonitor *monitor_info_ = nullptr; }; extern "C" HRESULT __stdcall CreateDXGIFactory2(UINT Flags, REFIID riid, diff --git a/src/dxgi/dxgi_interfaces.h b/src/dxgi/dxgi_interfaces.h index e7e759e8..599307e8 100644 --- a/src/dxgi/dxgi_interfaces.h +++ b/src/dxgi/dxgi_interfaces.h @@ -4,6 +4,7 @@ #include "util_d3dkmt.h" #include "Metal.hpp" #include "com/com_guid.hpp" +#include "dxgi_object.hpp" DEFINE_COM_INTERFACE("acdf3ef1-b33a-4cb6-97bd-1c1974827e6d", IMTLDXGIAdapter) : public IDXGIAdapter4 { @@ -21,6 +22,22 @@ DEFINE_COM_INTERFACE("6bfa1657-9cb1-471a-a4fb-7cacf8a81207", IMTLDXGIDevice) IDXGISwapChain1 **ppSwapChain) = 0; }; +struct MTLDXGI_MONITOR_DATA; +namespace dxmt { +class MTLDXGIMonitor; +}; + +DEFINE_COM_INTERFACE("8f804acf-f020-4914-98d4-a951da684b7f", IMTLDXGIMonitor) + : public IUnknown { + virtual HRESULT STDMETHODCALLTYPE SetMonitorData(const MTLDXGI_MONITOR_DATA *pData) = 0; + virtual HRESULT STDMETHODCALLTYPE GetMonitorData(MTLDXGI_MONITOR_DATA **ppData) = 0; +}; + +DEFINE_COM_INTERFACE("413dae46-5707-46e1-b66d-6e58b9f15113", IMTLDXGIFactory) + : public IDXGIFactory6 { + virtual dxmt::MTLDXGIMonitor * STDMETHODCALLTYPE GetMonitor() = 0; +}; + static constexpr IID DXMT_NVEXT_GUID = dxmt::guid::make_guid("ba0af616-4a43-4259-815c-db3b89829905"); namespace dxmt { diff --git a/src/dxgi/dxgi_monitor.cpp b/src/dxgi/dxgi_monitor.cpp new file mode 100644 index 00000000..69e0976f --- /dev/null +++ b/src/dxgi/dxgi_monitor.cpp @@ -0,0 +1,50 @@ +#include "dxgi_interfaces.h" +#include "dxgi_monitor.hpp" + +namespace dxmt { + +MTLDXGIMonitor::MTLDXGIMonitor(IUnknown *pParent) : parent_(pParent) { + monitorData.gammaCurve.gammaIsIdentity = true; +} + +ULONG +STDMETHODCALLTYPE +MTLDXGIMonitor::AddRef() { + return parent_->AddRef(); +} + +ULONG +STDMETHODCALLTYPE +MTLDXGIMonitor::Release() { + return parent_->Release(); +} + +HRESULT +STDMETHODCALLTYPE +MTLDXGIMonitor::QueryInterface(REFIID riid, void **ppvObject) { + return parent_->QueryInterface(riid, ppvObject); +} + +HRESULT +STDMETHODCALLTYPE +MTLDXGIMonitor::SetMonitorData(const MTLDXGI_MONITOR_DATA *pData) { + if (!pData) + return DXGI_ERROR_INVALID_CALL; + + monitorData = *pData; + + return S_OK; +} + +HRESULT +STDMETHODCALLTYPE +MTLDXGIMonitor::GetMonitorData(MTLDXGI_MONITOR_DATA **ppData) { + if (!ppData) + return DXGI_ERROR_INVALID_CALL; + + *ppData = &monitorData; + + return S_OK; +} + +} // namespace dxmt diff --git a/src/dxgi/dxgi_monitor.hpp b/src/dxgi/dxgi_monitor.hpp new file mode 100644 index 00000000..2a8c6c6c --- /dev/null +++ b/src/dxgi/dxgi_monitor.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "dxgi.h" +#include "dxgi_interfaces.h" +#include "dxmt_presenter.hpp" + +struct MTLDXGI_MONITOR_DATA { + dxmt::DXMTGammaCurve gammaCurve; +}; + +namespace dxmt { + +class MTLDXGIMonitor : public IMTLDXGIMonitor { +public: + MTLDXGIMonitor(IUnknown* pParent); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + + HRESULT STDMETHODCALLTYPE SetMonitorData(const MTLDXGI_MONITOR_DATA *pData); + HRESULT STDMETHODCALLTYPE GetMonitorData(MTLDXGI_MONITOR_DATA **ppData); + +private: + IUnknown *parent_; + MTLDXGI_MONITOR_DATA monitorData{}; +}; + +} // namespace dxmt diff --git a/src/dxgi/dxgi_output.cpp b/src/dxgi/dxgi_output.cpp index 18275389..d7e0376f 100644 --- a/src/dxgi/dxgi_output.cpp +++ b/src/dxgi/dxgi_output.cpp @@ -4,6 +4,7 @@ #include "com/com_guid.hpp" #include "com/com_pointer.hpp" #include "dxgi_interfaces.h" +#include "dxgi_monitor.hpp" #include "dxgi_object.hpp" #include "dxgi_options.hpp" #include "dxmt_format.hpp" @@ -13,6 +14,10 @@ namespace dxmt { +inline float GetGammaControlPointPosition(uint32_t cp_index) { + return float(cp_index) / float(DXMT_DXGI_GAMMA_CP_COUNT - 1); +} + /* * \see * https://github.com/microsoft/DirectXTex/blob/main/DirectXTex/DirectXTexUtil.cpp @@ -136,12 +141,21 @@ void FilterModesByDesc(std::vector &Modes, class MTLDXGIOutput : public MTLDXGIObject { public: - MTLDXGIOutput(IMTLDXGIAdapter *adapter, HMONITOR monitor, DxgiOptions &options) - : adapter_(adapter), monitor_(monitor), options_(options) { + MTLDXGIOutput(IMTLDXGIAdapter *adapter, IMTLDXGIFactory *factory, HMONITOR monitor, DxgiOptions &options) + : adapter_(adapter), factory_(factory), monitor_(monitor), options_(options) { WMTGetDisplayDescription(monitor_ == wsi::getDefaultMonitor() ? WMTGetPrimaryDisplayId() : WMTGetSecondaryDisplayId(), &native_desc_); + monitor_info_ = factory_->GetMonitor(); + MTLDXGI_MONITOR_DATA monitorData; + for (uint32_t i = 0; i < DXMT_DXGI_GAMMA_CP_COUNT; i++) { + float pos = GetGammaControlPointPosition(i); + monitorData.gammaCurve.Red[i] = monitorData.gammaCurve.Green[i] = monitorData.gammaCurve.Blue[i] = pos; + } + monitorData.gammaCurve.gammaIsIdentity = true; + monitorData.gammaCurve.updated = false; + monitor_info_->SetMonitorData(&monitorData); } ~MTLDXGIOutput() {} @@ -287,22 +301,56 @@ class MTLDXGIOutput : public MTLDXGIObject { gamma_caps->ScaleAndOffsetSupported = false; gamma_caps->MaxConvertedValue = 1.0f; gamma_caps->MinConvertedValue = 0.0f; - gamma_caps->NumGammaControlPoints = 1; + gamma_caps->NumGammaControlPoints = DXMT_DXGI_GAMMA_CP_COUNT; + for (uint32_t i = 0; i < gamma_caps->NumGammaControlPoints; i++) + gamma_caps->ControlPointPositions[i] = GetGammaControlPointPosition(i); return S_OK; } HRESULT STDMETHODCALLTYPE SetGammaControl(const DXGI_GAMMA_CONTROL *gamma_control) final { - ERR("Not implemented"); - return E_NOTIMPL; + MTLDXGI_MONITOR_DATA *monitorData = nullptr; + if (gamma_control == nullptr) + return E_NOTIMPL; + + HRESULT hr = monitor_info_->GetMonitorData(&monitorData); + if (FAILED(hr)) + return hr; + + for (uint32_t i = 0; i < DXMT_DXGI_GAMMA_CP_COUNT; i++) { + monitorData->gammaCurve.Red[i] = gamma_control->GammaCurve[i].Red; + monitorData->gammaCurve.Green[i] = gamma_control->GammaCurve[i].Green; + monitorData->gammaCurve.Blue[i] = gamma_control->GammaCurve[i].Blue; + float identity = GetGammaControlPointPosition(i); + monitorData->gammaCurve.gammaIsIdentity &= gamma_control->GammaCurve[i].Red == identity + && gamma_control->GammaCurve[i].Green == identity + && gamma_control->GammaCurve[i].Blue == identity; + } + monitorData->gammaCurve.updated = true; + + return S_OK; } HRESULT STDMETHODCALLTYPE GetGammaControl(DXGI_GAMMA_CONTROL *gamma_control) final { - ERR("Not implemented"); - return E_NOTIMPL; + MTLDXGI_MONITOR_DATA *monitorData = nullptr; + if (gamma_control == nullptr) + return E_NOTIMPL; + + HRESULT hr = monitor_info_->GetMonitorData(&monitorData); + if (FAILED(hr)) + return hr; + + gamma_control->Scale = { 1.0f, 1.0f, 1.0f }; + gamma_control->Offset = { 0.0f, 0.0f, 0.0f }; + for (uint32_t i = 0; i < DXMT_DXGI_GAMMA_CP_COUNT; i++) { + gamma_control->GammaCurve[i].Red = monitorData->gammaCurve.Red[i]; + gamma_control->GammaCurve[i].Green = monitorData->gammaCurve.Green[i]; + gamma_control->GammaCurve[i].Blue = monitorData->gammaCurve.Blue[i]; + } + return S_OK; } HRESULT @@ -603,13 +651,15 @@ class MTLDXGIOutput : public MTLDXGIObject { private: Com adapter_ = nullptr; + Com factory_ = nullptr; + MTLDXGIMonitor *monitor_info_ = nullptr; HMONITOR monitor_ = nullptr; WMTDisplayDescription native_desc_; DxgiOptions &options_; }; -Com CreateOutput(IMTLDXGIAdapter *pAadapter, HMONITOR monitor, DxgiOptions &options) { - return Com::transfer(new MTLDXGIOutput(pAadapter, monitor, options)); +Com CreateOutput(IMTLDXGIAdapter *pAadapter, IMTLDXGIFactory *pFactory, HMONITOR monitor, DxgiOptions &options) { + return Com::transfer(new MTLDXGIOutput(pAadapter, pFactory, monitor, options)); }; } // namespace dxmt \ No newline at end of file diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build index acf83471..8e61c17a 100644 --- a/src/dxgi/meson.build +++ b/src/dxgi/meson.build @@ -3,6 +3,7 @@ dxgi_res = wrc_generator.process('version.rc') dxgi_src = [ 'dxgi_adapter.cpp', 'dxgi_factory.cpp', + 'dxgi_monitor.cpp', 'dxgi_output.cpp', 'dxgi_options.cpp', 'dxgi.cpp', diff --git a/src/dxmt/dxmt_command.metal b/src/dxmt/dxmt_command.metal index a16384be..380bdad4 100644 --- a/src/dxmt/dxmt_command.metal +++ b/src/dxmt/dxmt_command.metal @@ -280,6 +280,7 @@ constexpr constant uint kPresentFCIndex_BackbufferIsSRGB = 0x103; constexpr constant uint kPresentFCIndex_HDRPQ = 0x101; constexpr constant uint kPresentFCIndex_WithHDRMetadata = 0x102; constexpr constant uint kPresentFCIndex_BackbufferIsMS = 0x104; +constexpr constant uint kPresentFCIndex_GammaEnabled = 0x105; constant bool present_backbuffer_size_matched [[function_constant(kPresentFCIndex_BackbufferSizeMatched)]]; constant bool present_backbuffer_is_srgb [[function_constant(kPresentFCIndex_BackbufferIsSRGB)]]; @@ -287,8 +288,10 @@ constant bool present_hdr_pq [[function_constant(kPresentFCIndex_HDRPQ)]]; constant bool present_with_hdr_metadata [[function_constant(kPresentFCIndex_WithHDRMetadata)]]; constant bool present_backbuffer_is_ms [[function_constant(kPresentFCIndex_BackbufferIsMS)]]; constant bool present_backbuffer_is_not_ms = !present_backbuffer_is_ms; +constant bool present_gamma_enabled [[function_constant(kPresentFCIndex_GammaEnabled)]]; constexpr sampler s(coord::normalized); +constexpr sampler gamma_sampler(coord::normalized, filter::linear, address::clamp_to_edge); struct DXMTPresentMetadata { float edr_scale; @@ -304,6 +307,7 @@ float3 to_srgb(float3 linear) { present_data input [[stage_in]], texture2d source [[texture(0), function_constant(present_backbuffer_is_not_ms)]], texture2d_ms source_ms [[texture(0), function_constant(present_backbuffer_is_ms)]], + texture2d gamma_lut [[texture(1), function_constant(present_gamma_enabled)]], constant DXMTPresentMetadata& meta [[buffer(0)]] ) { float4 output = float4(0); @@ -319,6 +323,12 @@ float3 to_srgb(float3 linear) { : source.sample(s, input.uv); } float3 output_rgb = output.xyz; + if (present_gamma_enabled && !present_hdr_pq && !present_with_hdr_metadata) { + output_rgb = float3( + gamma_lut.sample(gamma_sampler, float2(output_rgb.r, 0.5)).r, + gamma_lut.sample(gamma_sampler, float2(output_rgb.g, 0.5)).g, + gamma_lut.sample(gamma_sampler, float2(output_rgb.b, 0.5)).b); + } float edr_scale = meta.edr_scale; if (present_backbuffer_is_srgb) output_rgb = to_srgb(output_rgb); diff --git a/src/dxmt/dxmt_presenter.cpp b/src/dxmt/dxmt_presenter.cpp index 1bd4fcef..8f5777a6 100644 --- a/src/dxmt/dxmt_presenter.cpp +++ b/src/dxmt/dxmt_presenter.cpp @@ -4,21 +4,40 @@ #include "dxmt_format.hpp" #include "dxmt_presenter.hpp" #include "util_likely.hpp" +#include "winemetal.h" namespace dxmt { -Presenter::Presenter(WMT::Device device, WMT::MetalLayer layer, InternalCommandLibrary &lib, float scale_factor, uint8_t sample_count) : +Presenter::Presenter( + WMT::Device device, WMT::MetalLayer layer, InternalCommandLibrary &lib, float scale_factor, + uint8_t sample_count, DXMTGammaCurve *gamma_curve +) : device_(device), layer_(layer), lib_(lib), - sample_count_(sample_count) { + sample_count_(sample_count), + gamma_curve_(gamma_curve) { layer_.getProps(layer_props_); layer_props_.device = device; layer_props_.opaque = true; layer_props_.display_sync_enabled = false; layer_props_.framebuffer_only = false; // how strangely setting it true results in worse performance layer_props_.contents_scale = layer_props_.contents_scale * scale_factor; + + WMTTextureInfo texture_info; + texture_info.type = WMTTextureType2D; + texture_info.pixel_format = WMTPixelFormatRGBA32Float; + texture_info.usage = WMTTextureUsageShaderRead; + texture_info.options = WMTResourceStorageModeShared; + texture_info.width = DXMT_DXGI_GAMMA_CP_COUNT; + texture_info.height = 1; + texture_info.depth = 1; + texture_info.mipmap_level_count = 1; + texture_info.sample_count = 1; + texture_info.array_length = 1; + gamma_lut_texture_ = device.newTexture(texture_info); + gamma_lut_rgba_.reserve(DXMT_DXGI_GAMMA_CP_COUNT * 4); } bool @@ -113,6 +132,24 @@ Presenter::synchronizeLayerProperties() { return {metadata, ++frame_requested_, this}; } +void +Presenter::updateGammaLUT() { + if (gamma_curve_ && gamma_curve_->updated) { + gamma_curve_->updated = false; + for (uint32_t i = 0; i < DXMT_DXGI_GAMMA_CP_COUNT; i++) { + gamma_lut_rgba_[i * 4 + 0] = std::clamp(gamma_curve_->Red[i], 0.f, 1.f); + gamma_lut_rgba_[i * 4 + 1] = std::clamp(gamma_curve_->Green[i], 0.f, 1.f); + gamma_lut_rgba_[i * 4 + 2] = std::clamp(gamma_curve_->Blue[i], 0.f, 1.f); + gamma_lut_rgba_[i * 4 + 3] = 1.0f; + } + gamma_lut_texture_.replaceRegion( + {0, 0, 0}, {DXMT_DXGI_GAMMA_CP_COUNT, 1, 1}, 0, 0, + gamma_lut_rgba_.data(), + DXMT_DXGI_GAMMA_CP_COUNT * sizeof(float) * 4, + 0); + } +} + WMT::MetalDrawable Presenter::encodeCommands( WMT::CommandBuffer cmdbuf, WMT::Fence fence, WMT::Texture backbuffer, DXMTPresentMetadata metadata @@ -128,6 +165,7 @@ Presenter::encodeCommands( if (fence) encoder.waitForFence(fence, WMTRenderStageFragment); encoder.setFragmentTexture(backbuffer, 0); + encoder.setFragmentTexture(gamma_lut_texture_, 1); double width = layer_props_.drawable_width; double height = layer_props_.drawable_height; @@ -151,6 +189,7 @@ constexpr uint32_t kPresentFCIndex_HDRPQ = 0x101; constexpr uint32_t kPresentFCIndex_WithHDRMetadata = 0x102; constexpr uint32_t kPresentFCIndex_BackbufferIsSRGB = 0x103; constexpr uint32_t kPresentFCIndex_BackbufferIsMS = 0x104; +constexpr uint32_t kPresentFCIndex_GammaEnabled = 0x105; void Presenter::buildRenderPipelineState(bool is_pq, bool with_hdr_metadata, bool is_ms) { @@ -159,7 +198,7 @@ Presenter::buildRenderPipelineState(bool is_pq, bool with_hdr_metadata, bool is_ auto library = lib_.getLibrary(); uint32_t true_data = true, false_data = false; - WMTFunctionConstant constants[5]; + WMTFunctionConstant constants[6]; constants[0].data.set(&true_data); constants[0].type = WMTDataTypeBool; constants[0].index = kPresentFCIndex_BackbufferSizeMatched; @@ -175,6 +214,9 @@ Presenter::buildRenderPipelineState(bool is_pq, bool with_hdr_metadata, bool is_ constants[4].data.set(is_ms ? &true_data : &false_data); constants[4].type = WMTDataTypeBool; constants[4].index = kPresentFCIndex_BackbufferIsMS; + constants[5].data.set((gamma_curve_ && !gamma_curve_->gammaIsIdentity) ? &true_data : &false_data); + constants[5].type = WMTDataTypeBool; + constants[5].index = kPresentFCIndex_GammaEnabled; WMT::Reference error; auto vs_present_quad = library.newFunction("vs_present_quad"); diff --git a/src/dxmt/dxmt_presenter.hpp b/src/dxmt/dxmt_presenter.hpp index 08171f56..6f35a1ac 100644 --- a/src/dxmt/dxmt_presenter.hpp +++ b/src/dxmt/dxmt_presenter.hpp @@ -14,10 +14,20 @@ struct DXMTPresentMetadata { float max_display_luminance; }; +constexpr uint32_t DXMT_DXGI_GAMMA_CP_COUNT = 1024; + +struct DXMTGammaCurve { + float Red[DXMT_DXGI_GAMMA_CP_COUNT]; + float Green[DXMT_DXGI_GAMMA_CP_COUNT]; + float Blue[DXMT_DXGI_GAMMA_CP_COUNT]; + bool gammaIsIdentity; + bool updated; +}; + class Presenter : public RcObject { public: Presenter( - WMT::Device device, WMT::MetalLayer layer, InternalCommandLibrary &lib, float scale_factor, uint8_t sample_count + WMT::Device device, WMT::MetalLayer layer, InternalCommandLibrary &lib, float scale_factor, uint8_t sample_count, DXMTGammaCurve *gamma_curve ); bool changeLayerProperties( @@ -28,6 +38,8 @@ class Presenter : public RcObject { void changeHDRMetadata(const WMTHDRMetadata *metadata); + void updateGammaLUT(); + class PresentState { public: DXMTPresentMetadata metadata; @@ -73,6 +85,9 @@ class Presenter : public RcObject { WMTColorSpace display_colorspace_ = WMTColorSpaceSRGB; WMTHDRMetadata display_hdr_metadata_; WMTEDRValue display_edr_value_{0.0, 1.0}; + DXMTGammaCurve *gamma_curve_; + std::vector gamma_lut_rgba_; + WMT::Reference gamma_lut_texture_; WMT::Reference present_blit_; WMT::Reference present_scale_; std::atomic_flag pso_valid = 0;