Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/d3d11/d3d11_swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "wsi_window.hpp"
#include "dxmt_info.hpp"
#include "dxmt_presenter.hpp"
#include "dxgi_monitor.hpp"
#include <cfloat>
#include <format>

Expand Down Expand Up @@ -92,9 +93,20 @@ class MTLD3D11SwapChain final : public MTLDXGISubObject<IDXGISwapChain4, MTLD3D1
scale_factor = std::max(Config::getInstance().getOption<float>("d3d11.metalSpatialUpscaleFactor", 2), 1.0f);
}

DXMTGammaCurve *gamma_curve = nullptr;
Com<IMTLDXGIMonitor> 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,
Expand Down Expand Up @@ -596,6 +608,8 @@ class MTLD3D11SwapChain final : public MTLDXGISubObject<IDXGISwapChain4, MTLD3D1

std::unique_lock<d3d11_device_mutex> lock(device_->mutex);

presenter->updateGammaLUT();

device_context_->PrepareFlush();
auto &cmd_queue = device_->GetDXMTDevice().queue();
auto chunk = cmd_queue.CurrentChunk();
Expand Down
11 changes: 6 additions & 5 deletions src/dxgi/dxgi_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace dxmt {

Com<IDXGIOutput> CreateOutput(IMTLDXGIAdapter *pAadapter, HMONITOR monitor, DxgiOptions &options);
Com<IDXGIOutput> CreateOutput(IMTLDXGIAdapter *pAadapter, IMTLDXGIFactory *pFactory, HMONITOR monitor, DxgiOptions &options);

LUID GetAdapterLuid(WMT::Device device) {
// NOTE: use big-endian registryID, be consistent with MVK
Expand All @@ -20,7 +20,7 @@ LUID GetAdapterLuid(WMT::Device device) {

class MTLDXGIAdatper : public MTLDXGIObject<IMTLDXGIAdapter> {
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_);
Expand Down Expand Up @@ -190,9 +190,10 @@ class MTLDXGIAdatper : public MTLDXGIObject<IMTLDXGIAdapter> {
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;
Expand Down Expand Up @@ -274,13 +275,13 @@ class MTLDXGIAdatper : public MTLDXGIObject<IMTLDXGIAdapter> {
private:
WMT::Reference<WMT::Device> device_;
D3DKMT_HANDLE local_kmt_ = 0;
Com<IDXGIFactory> factory_;
Com<IMTLDXGIFactory> factory_;
DxgiOptions options_;
uint64_t mem_reserved_[2] = {0, 0};
};

Com<IMTLDXGIAdapter> CreateAdapter(WMT::Device Device,
IDXGIFactory2 *pFactory, Config &config) {
IMTLDXGIFactory *pFactory, Config &config) {
return Com<IMTLDXGIAdapter>::transfer(
new MTLDXGIAdatper(Device, pFactory, config));
}
Expand Down
19 changes: 16 additions & 3 deletions src/dxgi/dxgi_factory.cpp
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -11,12 +12,12 @@
namespace dxmt {

Com<IMTLDXGIAdapter> CreateAdapter(WMT::Device Device,
IDXGIFactory2 *pFactory, Config &config);
IMTLDXGIFactory *pFactory, Config &config);

class MTLDXGIFactory : public MTLDXGIObject<IDXGIFactory6> {
class MTLDXGIFactory : public MTLDXGIObject<IMTLDXGIFactory> {

public:
MTLDXGIFactory(UINT Flags) : flags_(Flags) {};
MTLDXGIFactory(UINT Flags) : flags_(Flags), monitor_info_(new MTLDXGIMonitor(this)) {};

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void **ppvObject) final {
Expand All @@ -34,6 +35,11 @@ class MTLDXGIFactory : public MTLDXGIObject<IDXGIFactory6> {
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));
}
Expand Down Expand Up @@ -301,10 +307,17 @@ class MTLDXGIFactory : public MTLDXGIObject<IDXGIFactory6> {
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,
Expand Down
17 changes: 17 additions & 0 deletions src/dxgi/dxgi_interfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
};

Comment on lines +25 to +40
Copy link
Owner

Choose a reason for hiding this comment

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

I'm strongly against introducing new COM interfaces and I've been always working on removing existing one. The only valid use case is when I want to export DXMT functionality for 3rd party users, which doesn't apply here.

Copy link
Owner

Choose a reason for hiding this comment

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

Honestly the whole concept of monitor is unnecessary. How is it different from output?

static constexpr IID DXMT_NVEXT_GUID = dxmt::guid::make_guid("ba0af616-4a43-4259-815c-db3b89829905");

namespace dxmt {
Expand Down
50 changes: 50 additions & 0 deletions src/dxgi/dxgi_monitor.cpp
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions src/dxgi/dxgi_monitor.hpp
Original file line number Diff line number Diff line change
@@ -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
68 changes: 59 additions & 9 deletions src/dxgi/dxgi_output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -136,12 +141,21 @@ void FilterModesByDesc(std::vector<DXGI_MODE_DESC1> &Modes,

class MTLDXGIOutput : public MTLDXGIObject<IDXGIOutput6> {
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();
Copy link
Owner

Choose a reason for hiding this comment

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

Why GetMonitor() here but QueryInterface() in other place?

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);
Comment on lines +150 to +158
Copy link
Owner

Choose a reason for hiding this comment

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

So the "source of truth" for gamma curve is from IDXGIFactory/Monitor instead of IDXGIOutput? Shouldn't the IDXGISwapChain pull gamma curve from IDXGIOutput for every Present()? That means gamma curve should not be passed to constructor of Presenter but as a parameter (const reference/pointer) of Presenter::synchronizeLayerProperties() and Presenter holds its own copy of MTLDXGI_MONITOR_DATA for change detection.

Copy link
Owner

Choose a reason for hiding this comment

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

Com<IDXGIOutput1> target_; to get corresponding IDXGIOutput of swapchain in fullscreen state (where gamma correction makes sense). And it can be NULL in windowed state => no gamma correction. Actually I found another oversight (not in this PR), that in SetFullscreenState() the second parameter of target output can be NULL: SetFullscreenState(TRUE, NULL) is valid, but DXGI will choose the output based on the swap-chain's device and the output window's placement. per MSDN.

Copy link
Owner

Choose a reason for hiding this comment

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

I realized you may need to get access to some DXMT-internal methods from IDXGIOutput (i.e. get instance of MTLDXGIOutput), then you can check MTLD3D11Fence/MTLD3D11FenceImpl for reference and make appropriate modifications.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So the "source of truth" for gamma curve is from IDXGIFactory/Monitor instead of IDXGIOutput? Shouldn't the IDXGISwapChain pull gamma curve from IDXGIOutput for every Present()? That means gamma curve should not be passed to constructor of Presenter but as a parameter (const reference/pointer) of Presenter::synchronizeLayerProperties() and Presenter holds its own copy of MTLDXGI_MONITOR_DATA for change detection.

concept was a bit influenced from DXVK, due connection between MTLDXGIOutput and IDXGISwapChain. there are multiple instances MTLDXGIOutput, like from MTLDXGIAdatper::EnumOutputs. so its not reliable place to store gamma curve and fetch from IDXGISwapChain I didn't saw how I can pass gamma curve to IDXGISwapChain.

Copy link
Contributor Author

@aquadran aquadran Feb 2, 2026

Choose a reason for hiding this comment

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

So the "source of truth" for gamma curve is from IDXGIFactory/Monitor instead of IDXGIOutput? Shouldn't the IDXGISwapChain pull gamma curve from IDXGIOutput for every Present()? That means gamma curve should not be passed to constructor of Presenter but as a parameter (const reference/pointer) of Presenter::synchronizeLayerProperties() and Presenter holds its own copy of MTLDXGI_MONITOR_DATA for change detection.

I cannot fetch from IDXGIOutput, I need to pass through one instance like IDXGIFactory.

}

~MTLDXGIOutput() {}
Expand Down Expand Up @@ -287,22 +301,56 @@ class MTLDXGIOutput : public MTLDXGIObject<IDXGIOutput6> {
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
Expand Down Expand Up @@ -603,13 +651,15 @@ class MTLDXGIOutput : public MTLDXGIObject<IDXGIOutput6> {

private:
Com<IMTLDXGIAdapter> adapter_ = nullptr;
Com<IMTLDXGIFactory> factory_ = nullptr;
MTLDXGIMonitor *monitor_info_ = nullptr;
HMONITOR monitor_ = nullptr;
WMTDisplayDescription native_desc_;
DxgiOptions &options_;
};

Com<IDXGIOutput> CreateOutput(IMTLDXGIAdapter *pAadapter, HMONITOR monitor, DxgiOptions &options) {
return Com<IDXGIOutput>::transfer(new MTLDXGIOutput(pAadapter, monitor, options));
Com<IDXGIOutput> CreateOutput(IMTLDXGIAdapter *pAadapter, IMTLDXGIFactory *pFactory, HMONITOR monitor, DxgiOptions &options) {
return Com<IDXGIOutput>::transfer(new MTLDXGIOutput(pAadapter, pFactory, monitor, options));
};

} // namespace dxmt
1 change: 1 addition & 0 deletions src/dxgi/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading
Loading