2018-04-18 15:33:56 +02:00
|
|
|
#include <algorithm>
|
2019-06-10 14:07:36 -05:00
|
|
|
#include <numeric>
|
2018-04-18 15:33:56 +02:00
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include "dxgi_adapter.h"
|
2019-03-14 15:35:35 +01:00
|
|
|
#include "dxgi_factory.h"
|
2017-10-11 03:09:04 +02:00
|
|
|
#include "dxgi_output.h"
|
2018-04-10 22:07:25 +02:00
|
|
|
#include "dxgi_swapchain.h"
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
#include "../dxvk/dxvk_format.h"
|
|
|
|
|
2022-09-15 14:57:03 +02:00
|
|
|
#include "../util/util_misc.h"
|
|
|
|
#include "../util/util_sleep.h"
|
|
|
|
#include "../util/util_time.h"
|
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
namespace dxvk {
|
2023-01-06 10:34:08 +00:00
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
DxgiOutput::DxgiOutput(
|
2019-03-14 15:35:35 +01:00
|
|
|
const Com<DxgiFactory>& factory,
|
2018-04-12 01:51:28 +02:00
|
|
|
const Com<DxgiAdapter>& adapter,
|
2018-01-13 16:32:46 +01:00
|
|
|
HMONITOR monitor)
|
2019-03-14 15:35:35 +01:00
|
|
|
: m_monitorInfo(factory->GetMonitorInfo()),
|
|
|
|
m_adapter(adapter),
|
2018-01-13 16:32:46 +01:00
|
|
|
m_monitor(monitor) {
|
2022-11-17 17:21:38 +00:00
|
|
|
CacheMonitorData();
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxgiOutput::~DxgiOutput() {
|
2017-11-26 14:01:41 +01:00
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::QueryInterface(REFIID riid, void** ppvObject) {
|
2019-02-10 07:01:01 +00:00
|
|
|
if (ppvObject == nullptr)
|
|
|
|
return E_POINTER;
|
|
|
|
|
2018-04-02 12:52:02 +02:00
|
|
|
*ppvObject = nullptr;
|
|
|
|
|
|
|
|
if (riid == __uuidof(IUnknown)
|
|
|
|
|| riid == __uuidof(IDXGIObject)
|
2018-10-11 09:39:24 +02:00
|
|
|
|| riid == __uuidof(IDXGIOutput)
|
2018-10-11 09:57:56 +02:00
|
|
|
|| riid == __uuidof(IDXGIOutput1)
|
|
|
|
|| riid == __uuidof(IDXGIOutput2)
|
2018-10-11 10:58:26 +02:00
|
|
|
|| riid == __uuidof(IDXGIOutput3)
|
2019-09-20 16:43:01 +02:00
|
|
|
|| riid == __uuidof(IDXGIOutput4)
|
2020-04-28 15:00:57 +02:00
|
|
|
|| riid == __uuidof(IDXGIOutput5)
|
|
|
|
|| riid == __uuidof(IDXGIOutput6)) {
|
2018-04-02 12:52:02 +02:00
|
|
|
*ppvObject = ref(this);
|
|
|
|
return S_OK;
|
|
|
|
}
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2023-03-01 13:19:42 +01:00
|
|
|
if (logQueryInterfaceError(__uuidof(IDXGIOutput), riid)) {
|
|
|
|
Logger::warn("DxgiOutput::QueryInterface: Unknown interface query");
|
|
|
|
Logger::warn(str::format(riid));
|
|
|
|
}
|
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-28 11:56:58 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetParent(REFIID riid, void **ppParent) {
|
2017-10-11 03:09:04 +02:00
|
|
|
return m_adapter->QueryInterface(riid, ppParent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::FindClosestMatchingMode(
|
2017-10-11 03:09:04 +02:00
|
|
|
const DXGI_MODE_DESC *pModeToMatch,
|
|
|
|
DXGI_MODE_DESC *pClosestMatch,
|
|
|
|
IUnknown *pConcernedDevice) {
|
2018-10-11 09:39:24 +02:00
|
|
|
if (!pModeToMatch || !pClosestMatch)
|
2018-03-12 10:52:32 +03:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
2018-10-11 09:39:24 +02:00
|
|
|
|
|
|
|
DXGI_MODE_DESC1 modeToMatch;
|
|
|
|
modeToMatch.Width = pModeToMatch->Width;
|
|
|
|
modeToMatch.Height = pModeToMatch->Height;
|
|
|
|
modeToMatch.RefreshRate = pModeToMatch->RefreshRate;
|
|
|
|
modeToMatch.Format = pModeToMatch->Format;
|
|
|
|
modeToMatch.ScanlineOrdering = pModeToMatch->ScanlineOrdering;
|
|
|
|
modeToMatch.Scaling = pModeToMatch->Scaling;
|
|
|
|
modeToMatch.Stereo = FALSE;
|
|
|
|
|
|
|
|
DXGI_MODE_DESC1 closestMatch = { };
|
2018-03-12 10:52:32 +03:00
|
|
|
|
2018-10-11 09:39:24 +02:00
|
|
|
HRESULT hr = FindClosestMatchingMode1(
|
|
|
|
&modeToMatch, &closestMatch, pConcernedDevice);
|
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
pClosestMatch->Width = closestMatch.Width;
|
|
|
|
pClosestMatch->Height = closestMatch.Height;
|
|
|
|
pClosestMatch->RefreshRate = closestMatch.RefreshRate;
|
|
|
|
pClosestMatch->Format = closestMatch.Format;
|
|
|
|
pClosestMatch->ScanlineOrdering = closestMatch.ScanlineOrdering;
|
|
|
|
pClosestMatch->Scaling = closestMatch.Scaling;
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::FindClosestMatchingMode1(
|
|
|
|
const DXGI_MODE_DESC1* pModeToMatch,
|
|
|
|
DXGI_MODE_DESC1* pClosestMatch,
|
|
|
|
IUnknown* pConcernedDevice) {
|
|
|
|
if (!pModeToMatch || !pClosestMatch)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
if (pModeToMatch->Format == DXGI_FORMAT_UNKNOWN && !pConcernedDevice)
|
2018-03-12 10:52:32 +03:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
2019-06-10 14:07:36 -05:00
|
|
|
|
|
|
|
// Both or neither must be zero
|
|
|
|
if ((pModeToMatch->Width == 0) ^ (pModeToMatch->Height == 0))
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2022-08-09 13:11:53 +01:00
|
|
|
wsi::WsiMode activeWsiMode = { };
|
|
|
|
wsi::getCurrentDisplayMode(m_monitor, &activeWsiMode);
|
2020-03-04 11:24:12 +01:00
|
|
|
|
2022-08-09 13:11:53 +01:00
|
|
|
DXGI_MODE_DESC1 activeMode = ConvertDisplayMode(activeWsiMode);
|
2019-06-10 14:07:36 -05:00
|
|
|
|
|
|
|
DXGI_MODE_DESC1 defaultMode;
|
|
|
|
defaultMode.Width = 0;
|
|
|
|
defaultMode.Height = 0;
|
|
|
|
defaultMode.RefreshRate = { 0, 0 };
|
|
|
|
defaultMode.Format = DXGI_FORMAT_UNKNOWN;
|
|
|
|
defaultMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
|
|
|
defaultMode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
|
|
|
defaultMode.Stereo = pModeToMatch->Stereo;
|
|
|
|
|
|
|
|
if (pModeToMatch->ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED)
|
|
|
|
defaultMode.ScanlineOrdering = activeMode.ScanlineOrdering;
|
|
|
|
|
|
|
|
if (pModeToMatch->Scaling == DXGI_MODE_SCALING_UNSPECIFIED)
|
|
|
|
defaultMode.Scaling = activeMode.Scaling;
|
|
|
|
|
2018-03-24 13:42:23 +01:00
|
|
|
DXGI_FORMAT targetFormat = pModeToMatch->Format;
|
2019-06-10 14:07:36 -05:00
|
|
|
|
|
|
|
if (pModeToMatch->Format == DXGI_FORMAT_UNKNOWN) {
|
|
|
|
defaultMode.Format = activeMode.Format;
|
|
|
|
targetFormat = activeMode.Format;
|
2018-03-24 13:42:23 +01:00
|
|
|
}
|
2019-06-10 14:07:36 -05:00
|
|
|
|
|
|
|
if (!pModeToMatch->Width) {
|
|
|
|
defaultMode.Width = activeMode.Width;
|
|
|
|
defaultMode.Height = activeMode.Height;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pModeToMatch->RefreshRate.Numerator || !pModeToMatch->RefreshRate.Denominator) {
|
|
|
|
defaultMode.RefreshRate.Numerator = activeMode.RefreshRate.Numerator;
|
|
|
|
defaultMode.RefreshRate.Denominator = activeMode.RefreshRate.Denominator;
|
|
|
|
}
|
|
|
|
|
2018-03-24 11:31:39 +01:00
|
|
|
UINT modeCount = 0;
|
2018-10-11 09:39:24 +02:00
|
|
|
GetDisplayModeList1(targetFormat, DXGI_ENUM_MODES_SCALING, &modeCount, nullptr);
|
2018-03-24 11:31:39 +01:00
|
|
|
|
|
|
|
if (modeCount == 0) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: FindClosestMatchingMode: No modes found");
|
2018-03-12 10:52:32 +03:00
|
|
|
return DXGI_ERROR_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
2018-10-11 09:39:24 +02:00
|
|
|
std::vector<DXGI_MODE_DESC1> modes(modeCount);
|
|
|
|
GetDisplayModeList1(targetFormat, DXGI_ENUM_MODES_SCALING, &modeCount, modes.data());
|
2018-03-12 10:52:32 +03:00
|
|
|
|
2019-06-10 14:07:36 -05:00
|
|
|
FilterModesByDesc(modes, *pModeToMatch);
|
|
|
|
FilterModesByDesc(modes, defaultMode);
|
2018-06-14 11:32:10 +02:00
|
|
|
|
2019-06-10 14:07:36 -05:00
|
|
|
if (modes.empty())
|
|
|
|
return DXGI_ERROR_NOT_FOUND;
|
2018-06-14 11:32:10 +02:00
|
|
|
|
2019-06-10 14:07:36 -05:00
|
|
|
*pClosestMatch = modes[0];
|
2018-06-14 11:32:10 +02:00
|
|
|
|
2019-06-10 14:07:36 -05:00
|
|
|
Logger::debug(str::format(
|
|
|
|
"DXGI: For mode ",
|
|
|
|
pModeToMatch->Width, "x", pModeToMatch->Height, "@",
|
|
|
|
pModeToMatch->RefreshRate.Denominator ? (pModeToMatch->RefreshRate.Numerator / pModeToMatch->RefreshRate.Denominator) : 0,
|
|
|
|
" found closest mode ",
|
|
|
|
pClosestMatch->Width, "x", pClosestMatch->Height, "@",
|
|
|
|
pClosestMatch->RefreshRate.Denominator ? (pClosestMatch->RefreshRate.Numerator / pClosestMatch->RefreshRate.Denominator) : 0));
|
2018-03-12 10:52:32 +03:00
|
|
|
return S_OK;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
2018-10-11 09:39:24 +02:00
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetDesc(DXGI_OUTPUT_DESC *pDesc) {
|
2017-10-11 03:09:04 +02:00
|
|
|
if (pDesc == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
2020-04-28 15:00:57 +02:00
|
|
|
|
|
|
|
DXGI_OUTPUT_DESC1 desc;
|
|
|
|
HRESULT hr = GetDesc1(&desc);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
std::memcpy(pDesc->DeviceName, desc.DeviceName, sizeof(pDesc->DeviceName));
|
|
|
|
pDesc->DesktopCoordinates = desc.DesktopCoordinates;
|
|
|
|
pDesc->AttachedToDesktop = desc.AttachedToDesktop;
|
|
|
|
pDesc->Rotation = desc.Rotation;
|
|
|
|
pDesc->Monitor = desc.Monitor;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetDesc1(
|
|
|
|
DXGI_OUTPUT_DESC1* pDesc) {
|
|
|
|
if (pDesc == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2022-08-09 13:11:53 +01:00
|
|
|
if (!wsi::getDesktopCoordinates(m_monitor, &pDesc->DesktopCoordinates)) {
|
|
|
|
Logger::err("DXGI: Failed to query monitor coords");
|
2018-01-13 16:32:46 +01:00
|
|
|
return E_FAIL;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 13:11:53 +01:00
|
|
|
if (!wsi::getDisplayName(m_monitor, pDesc->DeviceName)) {
|
|
|
|
Logger::err("DXGI: Failed to query monitor name");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
2022-11-17 17:22:22 +00:00
|
|
|
pDesc->AttachedToDesktop = 1;
|
|
|
|
pDesc->Rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
|
|
|
|
pDesc->Monitor = m_monitor;
|
2023-08-26 08:24:09 +01:00
|
|
|
pDesc->BitsPerColor = 10;
|
2022-11-17 17:22:22 +00:00
|
|
|
// This should only return DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
|
|
|
|
// (HDR) if the user has the HDR setting enabled in Windows.
|
|
|
|
// Games can still punt into HDR mode by using CheckColorSpaceSupport
|
|
|
|
// and SetColorSpace1.
|
2023-01-03 13:49:59 +00:00
|
|
|
//
|
|
|
|
// We have no way of checking the actual Windows colorspace as the
|
|
|
|
// only public method for this *is* DXGI which we are re-implementing.
|
|
|
|
// So we just pick our color space based on the DXVK_HDR env var
|
|
|
|
// and the punting from SetColorSpace1.
|
|
|
|
pDesc->ColorSpace = m_monitorInfo->CurrentColorSpace();
|
2022-11-17 17:22:22 +00:00
|
|
|
pDesc->RedPrimary[0] = m_metadata.redPrimary[0];
|
|
|
|
pDesc->RedPrimary[1] = m_metadata.redPrimary[1];
|
|
|
|
pDesc->GreenPrimary[0] = m_metadata.greenPrimary[0];
|
|
|
|
pDesc->GreenPrimary[1] = m_metadata.greenPrimary[1];
|
|
|
|
pDesc->BluePrimary[0] = m_metadata.bluePrimary[0];
|
|
|
|
pDesc->BluePrimary[1] = m_metadata.bluePrimary[1];
|
|
|
|
pDesc->WhitePoint[0] = m_metadata.whitePoint[0];
|
|
|
|
pDesc->WhitePoint[1] = m_metadata.whitePoint[1];
|
|
|
|
pDesc->MinLuminance = m_metadata.minLuminance;
|
|
|
|
pDesc->MaxLuminance = m_metadata.maxLuminance;
|
|
|
|
pDesc->MaxFullFrameLuminance = m_metadata.maxFullFrameLuminance;
|
2017-10-11 03:09:04 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
2020-04-28 15:00:57 +02:00
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplayModeList(
|
2017-10-11 03:09:04 +02:00
|
|
|
DXGI_FORMAT EnumFormat,
|
|
|
|
UINT Flags,
|
2018-10-11 09:39:24 +02:00
|
|
|
UINT* pNumModes,
|
|
|
|
DXGI_MODE_DESC* pDesc) {
|
|
|
|
if (pNumModes == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
std::vector<DXGI_MODE_DESC1> modes;
|
|
|
|
|
|
|
|
if (pDesc)
|
2019-10-01 21:55:57 +02:00
|
|
|
modes.resize(std::max(1u, *pNumModes));
|
2018-10-11 09:39:24 +02:00
|
|
|
|
|
|
|
HRESULT hr = GetDisplayModeList1(
|
|
|
|
EnumFormat, Flags, pNumModes,
|
|
|
|
pDesc ? modes.data() : nullptr);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < *pNumModes && i < modes.size(); i++) {
|
|
|
|
pDesc[i].Width = modes[i].Width;
|
|
|
|
pDesc[i].Height = modes[i].Height;
|
|
|
|
pDesc[i].RefreshRate = modes[i].RefreshRate;
|
|
|
|
pDesc[i].Format = modes[i].Format;
|
|
|
|
pDesc[i].ScanlineOrdering = modes[i].ScanlineOrdering;
|
|
|
|
pDesc[i].Scaling = modes[i].Scaling;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplayModeList1(
|
|
|
|
DXGI_FORMAT EnumFormat,
|
|
|
|
UINT Flags,
|
|
|
|
UINT* pNumModes,
|
|
|
|
DXGI_MODE_DESC1* pDesc) {
|
2017-10-11 03:09:04 +02:00
|
|
|
if (pNumModes == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2019-10-01 21:48:13 +02:00
|
|
|
// Special case, just return zero modes
|
|
|
|
if (EnumFormat == DXGI_FORMAT_UNKNOWN) {
|
|
|
|
*pNumModes = 0;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
// Walk over all modes that the display supports and
|
|
|
|
// return those that match the requested format etc.
|
2022-08-09 13:11:53 +01:00
|
|
|
wsi::WsiMode devMode = { };
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
uint32_t srcModeId = 0;
|
|
|
|
uint32_t dstModeId = 0;
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-10-11 09:39:24 +02:00
|
|
|
std::vector<DXGI_MODE_DESC1> modeList;
|
2018-04-18 15:33:56 +02:00
|
|
|
|
2022-08-09 13:11:53 +01:00
|
|
|
while (wsi::getDisplayMode(m_monitor, srcModeId++, &devMode)) {
|
2022-08-17 19:37:19 +00:00
|
|
|
// Only enumerate interlaced modes if requested.
|
|
|
|
if (devMode.interlaced && !(Flags & DXGI_ENUM_MODES_INTERLACED))
|
2018-01-13 16:32:46 +01:00
|
|
|
continue;
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
// Skip modes with incompatible formats
|
2022-08-09 13:11:53 +01:00
|
|
|
if (devMode.bitsPerPixel != GetMonitorFormatBpp(EnumFormat))
|
2018-01-13 16:32:46 +01:00
|
|
|
continue;
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
if (pDesc != nullptr) {
|
2022-08-09 13:11:53 +01:00
|
|
|
DXGI_MODE_DESC1 mode = ConvertDisplayMode(devMode);
|
|
|
|
// Fix up the DXGI_FORMAT to match what we were enumerating.
|
|
|
|
mode.Format = EnumFormat;
|
|
|
|
|
2018-04-18 15:33:56 +02:00
|
|
|
modeList.push_back(mode);
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
2018-01-13 16:32:46 +01:00
|
|
|
|
|
|
|
dstModeId += 1;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
2018-04-18 15:33:56 +02:00
|
|
|
// Sort display modes by width, height and refresh rate,
|
|
|
|
// in that order. Some games rely on correct ordering.
|
|
|
|
std::sort(modeList.begin(), modeList.end(),
|
2018-10-11 09:39:24 +02:00
|
|
|
[] (const DXGI_MODE_DESC1& a, const DXGI_MODE_DESC1& b) {
|
2018-04-18 15:33:56 +02:00
|
|
|
if (a.Width < b.Width) return true;
|
|
|
|
if (a.Width > b.Width) return false;
|
|
|
|
|
|
|
|
if (a.Height < b.Height) return true;
|
|
|
|
if (a.Height > b.Height) return false;
|
|
|
|
|
|
|
|
return (a.RefreshRate.Numerator / a.RefreshRate.Denominator)
|
|
|
|
< (b.RefreshRate.Numerator / b.RefreshRate.Denominator);
|
|
|
|
});
|
|
|
|
|
|
|
|
// If requested, write out the first set of display
|
|
|
|
// modes to the destination array.
|
|
|
|
if (pDesc != nullptr) {
|
|
|
|
for (uint32_t i = 0; i < *pNumModes && i < dstModeId; i++)
|
|
|
|
pDesc[i] = modeList[i];
|
|
|
|
|
|
|
|
if (dstModeId > *pNumModes)
|
|
|
|
return DXGI_ERROR_MORE_DATA;
|
|
|
|
}
|
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
*pNumModes = dstModeId;
|
|
|
|
return S_OK;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
2018-10-11 09:39:24 +02:00
|
|
|
|
|
|
|
|
2018-04-12 13:38:22 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData(IDXGISurface* pDestination) {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::err("DxgiOutput::GetDisplaySurfaceData: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-12 13:38:22 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {
|
2018-12-11 15:32:06 +01:00
|
|
|
DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
|
2019-03-14 15:35:35 +01:00
|
|
|
HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);
|
2018-12-11 15:32:06 +01:00
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
return hr;
|
2021-05-20 23:06:11 +02:00
|
|
|
|
2023-07-31 21:47:44 +02:00
|
|
|
// Need to acquire swap chain and unlock monitor data, since querying
|
|
|
|
// frame statistics from the swap chain will also access monitor data.
|
|
|
|
Com<IDXGISwapChain> swapChain = monitorInfo->pSwapChain;
|
|
|
|
m_monitorInfo->ReleaseMonitorData();
|
2022-09-15 14:58:31 +02:00
|
|
|
|
2023-07-31 21:47:44 +02:00
|
|
|
// This API only works if there is a full-screen swap chain active.
|
|
|
|
if (swapChain == nullptr) {
|
|
|
|
*pStats = DXGI_FRAME_STATISTICS();
|
|
|
|
return S_OK;
|
|
|
|
}
|
2021-05-20 23:06:11 +02:00
|
|
|
|
2023-07-31 21:47:44 +02:00
|
|
|
return swapChain->GetFrameStatistics(pStats);
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-12 13:38:22 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL* pArray) {
|
2018-12-11 15:32:06 +01:00
|
|
|
DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
|
2019-03-14 15:35:35 +01:00
|
|
|
HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);
|
2018-12-11 15:32:06 +01:00
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
return hr;
|
2018-04-12 13:38:22 +02:00
|
|
|
|
2018-12-11 15:32:06 +01:00
|
|
|
*pArray = monitorInfo->GammaCurve;
|
2019-03-14 15:35:35 +01:00
|
|
|
m_monitorInfo->ReleaseMonitorData();
|
2018-04-12 13:38:22 +02:00
|
|
|
return S_OK;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-12 13:38:22 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControlCapabilities(DXGI_GAMMA_CONTROL_CAPABILITIES* pGammaCaps) {
|
2018-04-15 20:12:41 +02:00
|
|
|
pGammaCaps->ScaleAndOffsetSupported = FALSE;
|
2018-04-10 22:07:25 +02:00
|
|
|
pGammaCaps->MaxConvertedValue = 1.0f;
|
|
|
|
pGammaCaps->MinConvertedValue = 0.0f;
|
2018-04-13 13:47:15 +02:00
|
|
|
pGammaCaps->NumGammaControlPoints = DXGI_VK_GAMMA_CP_COUNT;
|
2018-04-10 22:07:25 +02:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < pGammaCaps->NumGammaControlPoints; i++)
|
2018-04-13 13:47:15 +02:00
|
|
|
pGammaCaps->ControlPointPositions[i] = GammaControlPointLocation(i);
|
2018-04-10 22:07:25 +02:00
|
|
|
return S_OK;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
void STDMETHODCALLTYPE DxgiOutput::ReleaseOwnership() {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::warn("DxgiOutput::ReleaseOwnership: Stub");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-12 13:38:22 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::SetDisplaySurface(IDXGISurface* pScanoutSurface) {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::err("DxgiOutput::SetDisplaySurface: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
2018-10-11 09:39:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData1(IDXGIResource* pDestination) {
|
|
|
|
Logger::err("DxgiOutput::SetDisplaySurface1: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
2017-10-11 03:09:04 +02:00
|
|
|
|
|
|
|
|
2018-04-12 13:38:22 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::SetGammaControl(const DXGI_GAMMA_CONTROL* pArray) {
|
2018-12-11 15:32:06 +01:00
|
|
|
DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
|
2019-03-14 15:35:35 +01:00
|
|
|
HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);
|
2018-12-11 15:32:06 +01:00
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
return hr;
|
2018-04-12 13:38:22 +02:00
|
|
|
|
2018-12-11 15:32:06 +01:00
|
|
|
monitorInfo->GammaCurve = *pArray;
|
|
|
|
|
|
|
|
if (monitorInfo->pSwapChain) {
|
|
|
|
hr = monitorInfo->pSwapChain->SetGammaControl(
|
|
|
|
DXGI_VK_GAMMA_CP_COUNT, pArray->GammaCurve);
|
2018-04-12 13:38:22 +02:00
|
|
|
}
|
2018-12-11 15:32:06 +01:00
|
|
|
|
2019-03-14 15:35:35 +01:00
|
|
|
m_monitorInfo->ReleaseMonitorData();
|
2018-12-11 15:32:06 +01:00
|
|
|
return hr;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::TakeOwnership(
|
2017-10-11 03:09:04 +02:00
|
|
|
IUnknown *pDevice,
|
|
|
|
BOOL Exclusive) {
|
|
|
|
Logger::warn("DxgiOutput::TakeOwnership: Stub");
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::WaitForVBlank() {
|
2018-10-09 19:17:20 +02:00
|
|
|
static bool s_errorShown = false;
|
2018-10-09 14:48:49 +02:00
|
|
|
|
2018-10-09 19:17:20 +02:00
|
|
|
if (!std::exchange(s_errorShown, true))
|
2022-09-15 14:57:03 +02:00
|
|
|
Logger::warn("DxgiOutput::WaitForVBlank: Inaccurate");
|
|
|
|
|
|
|
|
// Get monitor data to compute the sleep duration
|
|
|
|
DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
|
|
|
|
HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);
|
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
// Estimate number of vblanks since last mode
|
|
|
|
// change, then wait for one more refresh period
|
|
|
|
auto refreshPeriod = computeRefreshPeriod(
|
|
|
|
monitorInfo->LastMode.RefreshRate.Numerator,
|
|
|
|
monitorInfo->LastMode.RefreshRate.Denominator);
|
|
|
|
|
|
|
|
auto t0 = dxvk::high_resolution_clock::get_time_from_counter(monitorInfo->FrameStats.SyncQPCTime.QuadPart);
|
|
|
|
auto t1 = dxvk::high_resolution_clock::now();
|
|
|
|
|
|
|
|
uint64_t vblankCount = computeRefreshCount(t0, t1, refreshPeriod);
|
|
|
|
auto t2 = t0 + (vblankCount + 1) * refreshPeriod;
|
|
|
|
|
|
|
|
m_monitorInfo->ReleaseMonitorData();
|
|
|
|
|
|
|
|
// Sleep until the given time point
|
|
|
|
Sleep::sleepUntil(t1, t2);
|
2017-10-11 03:09:04 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
|
2018-10-11 09:39:24 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::DuplicateOutput(
|
|
|
|
IUnknown* pDevice,
|
|
|
|
IDXGIOutputDuplication** ppOutputDuplication) {
|
2019-09-20 16:43:01 +02:00
|
|
|
return DuplicateOutput1(pDevice, 0, 0, nullptr, ppOutputDuplication);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::DuplicateOutput1(
|
|
|
|
IUnknown* pDevice,
|
|
|
|
UINT Flags,
|
|
|
|
UINT SupportedFormatsCount,
|
|
|
|
const DXGI_FORMAT* pSupportedFormats,
|
|
|
|
IDXGIOutputDuplication** ppOutputDuplication) {
|
|
|
|
InitReturnPtr(ppOutputDuplication);
|
|
|
|
|
|
|
|
if (!pDevice)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
2018-10-11 09:39:24 +02:00
|
|
|
static bool s_errorShown = false;
|
|
|
|
|
|
|
|
if (!std::exchange(s_errorShown, true))
|
2019-09-20 16:43:01 +02:00
|
|
|
Logger::err("DxgiOutput::DuplicateOutput1: Not implemented");
|
2018-10-11 09:39:24 +02:00
|
|
|
|
2019-09-20 16:43:01 +02:00
|
|
|
// At least return a valid error code
|
|
|
|
return DXGI_ERROR_UNSUPPORTED;
|
2018-10-11 09:39:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-11 09:57:56 +02:00
|
|
|
BOOL DxgiOutput::SupportsOverlays() {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::CheckOverlaySupport(
|
|
|
|
DXGI_FORMAT EnumFormat,
|
|
|
|
IUnknown* pConcernedDevice,
|
|
|
|
UINT* pFlags) {
|
|
|
|
Logger::warn("DxgiOutput: CheckOverlaySupport: Stub");
|
|
|
|
return DXGI_ERROR_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-11 10:58:26 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::CheckOverlayColorSpaceSupport(
|
|
|
|
DXGI_FORMAT Format,
|
|
|
|
DXGI_COLOR_SPACE_TYPE ColorSpace,
|
|
|
|
IUnknown* pConcernedDevice,
|
|
|
|
UINT* pFlags) {
|
|
|
|
Logger::warn("DxgiOutput: CheckOverlayColorSpaceSupport: Stub");
|
|
|
|
return DXGI_ERROR_UNSUPPORTED;
|
|
|
|
}
|
2018-01-13 16:32:46 +01:00
|
|
|
|
2019-06-10 14:07:36 -05:00
|
|
|
|
2020-04-28 15:00:57 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::CheckHardwareCompositionSupport(
|
|
|
|
UINT* pFlags) {
|
|
|
|
Logger::warn("DxgiOutput: CheckHardwareCompositionSupport: Stub");
|
|
|
|
|
|
|
|
*pFlags = 0;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-10 14:07:36 -05:00
|
|
|
void DxgiOutput::FilterModesByDesc(
|
|
|
|
std::vector<DXGI_MODE_DESC1>& Modes,
|
|
|
|
const DXGI_MODE_DESC1& TargetMode) {
|
2021-12-15 21:54:37 +01:00
|
|
|
// Filter modes based on format properties
|
2019-06-11 15:45:32 +02:00
|
|
|
bool testScanlineOrder = false;
|
|
|
|
bool testScaling = false;
|
|
|
|
bool testFormat = false;
|
|
|
|
|
|
|
|
for (const auto& mode : Modes) {
|
|
|
|
testScanlineOrder |= TargetMode.ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED
|
|
|
|
&& TargetMode.ScanlineOrdering == mode.ScanlineOrdering;
|
|
|
|
testScaling |= TargetMode.Scaling != DXGI_MODE_SCALING_UNSPECIFIED
|
|
|
|
&& TargetMode.Scaling == mode.Scaling;
|
|
|
|
testFormat |= TargetMode.Format != DXGI_FORMAT_UNKNOWN
|
|
|
|
&& TargetMode.Format == mode.Format;
|
|
|
|
}
|
|
|
|
|
2019-06-10 14:07:36 -05:00
|
|
|
for (auto it = Modes.begin(); it != Modes.end(); ) {
|
|
|
|
bool skipMode = it->Stereo != TargetMode.Stereo;
|
|
|
|
|
2019-06-11 15:45:32 +02:00
|
|
|
if (testScanlineOrder)
|
2019-06-10 14:07:36 -05:00
|
|
|
skipMode |= it->ScanlineOrdering != TargetMode.ScanlineOrdering;
|
|
|
|
|
2019-06-11 15:45:32 +02:00
|
|
|
if (testScaling)
|
2019-06-10 14:07:36 -05:00
|
|
|
skipMode |= it->Scaling != TargetMode.Scaling;
|
|
|
|
|
2019-06-11 15:45:32 +02:00
|
|
|
if (testFormat)
|
|
|
|
skipMode |= it->Format != TargetMode.Format;
|
2019-06-10 14:07:36 -05:00
|
|
|
|
2021-12-15 21:54:37 +01:00
|
|
|
it = skipMode ? Modes.erase(it) : ++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Filter by closest resolution
|
|
|
|
uint32_t minDiffResolution = 0;
|
|
|
|
|
|
|
|
if (TargetMode.Width) {
|
|
|
|
minDiffResolution = std::accumulate(
|
|
|
|
Modes.begin(), Modes.end(), std::numeric_limits<uint32_t>::max(),
|
|
|
|
[&TargetMode] (uint32_t current, const DXGI_MODE_DESC1& mode) {
|
|
|
|
uint32_t diff = std::abs(int32_t(TargetMode.Width - mode.Width))
|
|
|
|
+ std::abs(int32_t(TargetMode.Height - mode.Height));
|
|
|
|
return std::min(current, diff);
|
|
|
|
});
|
|
|
|
|
|
|
|
for (auto it = Modes.begin(); it != Modes.end(); ) {
|
2019-06-10 14:07:36 -05:00
|
|
|
uint32_t diff = std::abs(int32_t(TargetMode.Width - it->Width))
|
|
|
|
+ std::abs(int32_t(TargetMode.Height - it->Height));
|
2021-12-15 21:54:37 +01:00
|
|
|
|
|
|
|
bool skipMode = diff != minDiffResolution;
|
|
|
|
it = skipMode ? Modes.erase(it) : ++it;
|
2019-06-10 14:07:36 -05:00
|
|
|
}
|
2021-12-15 21:54:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Filter by closest refresh rate
|
|
|
|
uint32_t minDiffRefreshRate = 0;
|
2019-06-10 14:07:36 -05:00
|
|
|
|
2021-12-15 21:54:37 +01:00
|
|
|
if (TargetMode.RefreshRate.Numerator && TargetMode.RefreshRate.Denominator) {
|
|
|
|
minDiffRefreshRate = std::accumulate(
|
|
|
|
Modes.begin(), Modes.end(), std::numeric_limits<uint64_t>::max(),
|
|
|
|
[&TargetMode] (uint64_t current, const DXGI_MODE_DESC1& mode) {
|
|
|
|
uint64_t rate = uint64_t(mode.RefreshRate.Numerator)
|
|
|
|
* uint64_t(TargetMode.RefreshRate.Denominator)
|
|
|
|
/ uint64_t(mode.RefreshRate.Denominator);
|
|
|
|
uint64_t diff = std::abs(int64_t(rate - uint64_t(TargetMode.RefreshRate.Numerator)));
|
|
|
|
return std::min(current, diff);
|
|
|
|
});
|
|
|
|
|
|
|
|
for (auto it = Modes.begin(); it != Modes.end(); ) {
|
2019-10-29 08:42:53 +01:00
|
|
|
uint64_t rate = uint64_t(it->RefreshRate.Numerator)
|
|
|
|
* uint64_t(TargetMode.RefreshRate.Denominator)
|
|
|
|
/ uint64_t(it->RefreshRate.Denominator);
|
|
|
|
uint64_t diff = std::abs(int64_t(rate - uint64_t(TargetMode.RefreshRate.Numerator)));
|
2019-06-10 14:07:36 -05:00
|
|
|
|
2021-12-15 21:54:37 +01:00
|
|
|
bool skipMode = diff != minDiffRefreshRate;
|
|
|
|
it = skipMode ? Modes.erase(it) : ++it;
|
|
|
|
}
|
2019-06-10 14:07:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 17:21:38 +00:00
|
|
|
|
|
|
|
void DxgiOutput::CacheMonitorData() {
|
|
|
|
// Try and find an existing monitor info.
|
|
|
|
DXGI_VK_MONITOR_DATA* pMonitorData;
|
|
|
|
if (SUCCEEDED(m_monitorInfo->AcquireMonitorData(m_monitor, &pMonitorData))) {
|
|
|
|
m_metadata = pMonitorData->DisplayMetadata;
|
2022-11-21 20:24:15 +01:00
|
|
|
m_monitorInfo->ReleaseMonitorData();
|
2022-11-17 17:21:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init monitor info ourselves.
|
|
|
|
//
|
|
|
|
// If some other thread ends up beating us to it
|
|
|
|
// by another InitMonitorData, it doesn't really matter.
|
|
|
|
//
|
|
|
|
// The only thing we cache from this is the m_metadata which
|
|
|
|
// should be exactly the same.
|
|
|
|
// We don't store any pointers from the DXGI_VK_MONITOR_DATA
|
|
|
|
// sturcture, etc.
|
|
|
|
DXGI_VK_MONITOR_DATA monitorData = {};
|
|
|
|
|
|
|
|
// Query current display mode
|
|
|
|
wsi::WsiMode activeWsiMode = { };
|
|
|
|
wsi::getCurrentDisplayMode(m_monitor, &activeWsiMode);
|
|
|
|
|
|
|
|
// Get the display metadata + colorimetry
|
|
|
|
wsi::WsiEdidData edidData = wsi::getMonitorEdid(m_monitor);
|
|
|
|
std::optional<wsi::WsiDisplayMetadata> metadata = std::nullopt;
|
|
|
|
if (!edidData.empty())
|
|
|
|
metadata = wsi::parseColorimetryInfo(edidData);
|
|
|
|
|
|
|
|
if (metadata)
|
|
|
|
m_metadata = metadata.value();
|
|
|
|
else
|
|
|
|
Logger::err("DXGI: Failed to parse display metadata + colorimetry info, using blank.");
|
|
|
|
|
2023-01-06 10:34:08 +00:00
|
|
|
// Normalize either the display metadata we got back, or our
|
|
|
|
// blank one to get something sane here.
|
2023-05-15 17:21:55 +00:00
|
|
|
NormalizeDisplayMetadata(m_monitorInfo->DefaultColorSpace() != DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, m_metadata);
|
2023-01-06 10:34:08 +00:00
|
|
|
|
2023-06-16 16:05:38 +02:00
|
|
|
auto refreshPeriod = computeRefreshPeriod(
|
|
|
|
activeWsiMode.refreshRate.numerator,
|
|
|
|
activeWsiMode.refreshRate.denominator);
|
|
|
|
|
2022-11-17 17:21:38 +00:00
|
|
|
monitorData.FrameStats.SyncQPCTime.QuadPart = dxvk::high_resolution_clock::get_counter();
|
2023-06-16 16:05:38 +02:00
|
|
|
monitorData.FrameStats.SyncRefreshCount = computeRefreshCount(
|
|
|
|
dxvk::high_resolution_clock::time_point(),
|
|
|
|
dxvk::high_resolution_clock::get_time_from_counter(monitorData.FrameStats.SyncQPCTime.QuadPart),
|
|
|
|
refreshPeriod);
|
|
|
|
|
|
|
|
monitorData.FrameStats.PresentRefreshCount = monitorData.FrameStats.SyncRefreshCount;
|
2022-11-17 17:21:38 +00:00
|
|
|
monitorData.GammaCurve.Scale = { 1.0f, 1.0f, 1.0f };
|
|
|
|
monitorData.GammaCurve.Offset = { 0.0f, 0.0f, 0.0f };
|
|
|
|
monitorData.LastMode = ConvertDisplayMode(activeWsiMode);
|
|
|
|
monitorData.DisplayMetadata = m_metadata;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < DXGI_VK_GAMMA_CP_COUNT; i++) {
|
|
|
|
const float value = GammaControlPointLocation(i);
|
|
|
|
monitorData.GammaCurve.GammaCurve[i] = { value, value, value };
|
|
|
|
}
|
|
|
|
|
|
|
|
m_monitorInfo->InitMonitorData(m_monitor, &monitorData);
|
|
|
|
}
|
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|