2018-04-18 15:33:56 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include "dxgi_adapter.h"
|
|
|
|
#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"
|
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
namespace dxvk {
|
|
|
|
|
|
|
|
DxgiOutput::DxgiOutput(
|
2018-04-12 01:51:28 +02:00
|
|
|
const Com<DxgiAdapter>& adapter,
|
2018-01-13 16:32:46 +01:00
|
|
|
HMONITOR monitor)
|
|
|
|
: m_adapter(adapter),
|
|
|
|
m_monitor(monitor) {
|
2018-04-12 13:38:22 +02:00
|
|
|
// 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 };
|
|
|
|
|
2018-04-13 13:47:15 +02:00
|
|
|
for (uint32_t i = 0; i < DXGI_VK_GAMMA_CP_COUNT; i++) {
|
|
|
|
const float value = GammaControlPointLocation(i);
|
2018-04-12 13:38:22 +02:00
|
|
|
outputData.GammaCurve.GammaCurve[i] = { value, value, value };
|
|
|
|
}
|
|
|
|
|
|
|
|
outputData.GammaDirty = FALSE;
|
|
|
|
m_adapter->SetOutputData(m_monitor, &outputData);
|
|
|
|
}
|
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) {
|
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)
|
|
|
|
|| riid == __uuidof(IDXGIOutput3)) {
|
2018-04-02 12:52:02 +02:00
|
|
|
*ppvObject = ref(this);
|
|
|
|
return S_OK;
|
|
|
|
}
|
2017-10-11 03:09:04 +02:00
|
|
|
|
|
|
|
Logger::warn("DxgiOutput::QueryInterface: Unknown interface query");
|
2018-03-12 12:05:43 +01:00
|
|
|
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 08:52:32 +01: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 08:52:32 +01: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 08:52:32 +01:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
2018-03-24 11:31:39 +01:00
|
|
|
|
|
|
|
// If no format was specified, fall back to a standard
|
|
|
|
// SRGB format, which is supported on all devices.
|
2018-03-24 13:42:23 +01:00
|
|
|
DXGI_FORMAT targetFormat = pModeToMatch->Format;
|
2018-03-24 11:31:39 +01:00
|
|
|
|
2018-03-24 13:42:23 +01:00
|
|
|
if (targetFormat == DXGI_FORMAT_UNKNOWN)
|
|
|
|
targetFormat = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
2018-04-29 23:03:27 +02:00
|
|
|
|
2018-03-24 13:42:23 +01:00
|
|
|
UINT targetRefreshRate = 0;
|
|
|
|
|
2018-04-29 23:03:27 +02:00
|
|
|
if (pModeToMatch->RefreshRate.Denominator != 0) {
|
2018-03-24 13:42:23 +01:00
|
|
|
targetRefreshRate = pModeToMatch->RefreshRate.Numerator
|
|
|
|
/ pModeToMatch->RefreshRate.Denominator;
|
|
|
|
}
|
2018-03-24 11:31:39 +01:00
|
|
|
|
|
|
|
// List all supported modes and filter
|
|
|
|
// out those we don't actually need
|
|
|
|
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 08:52:32 +01: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-24 13:42:23 +01:00
|
|
|
|
|
|
|
for (auto it = modes.begin(); it != modes.end(); ) {
|
|
|
|
bool skipMode = false;
|
2018-03-24 11:31:39 +01:00
|
|
|
|
2018-03-24 13:42:23 +01:00
|
|
|
// Remove modes with a different refresh rate
|
|
|
|
if (targetRefreshRate != 0) {
|
2018-03-24 11:31:39 +01:00
|
|
|
UINT modeRefreshRate = it->RefreshRate.Numerator
|
|
|
|
/ it->RefreshRate.Denominator;
|
2018-03-24 13:42:23 +01:00
|
|
|
skipMode |= modeRefreshRate != targetRefreshRate;
|
2018-03-12 08:52:32 +01:00
|
|
|
}
|
2018-03-24 13:42:23 +01:00
|
|
|
|
|
|
|
// Remove modes with incorrect scaling
|
|
|
|
if (pModeToMatch->Scaling != DXGI_MODE_SCALING_UNSPECIFIED)
|
|
|
|
skipMode |= it->Scaling != pModeToMatch->Scaling;
|
|
|
|
|
2018-10-11 09:39:24 +02:00
|
|
|
// Remove modes with incorrect stereo mode
|
|
|
|
skipMode |= it->Stereo != pModeToMatch->Stereo;
|
|
|
|
|
2018-03-24 13:42:23 +01:00
|
|
|
it = skipMode ? modes.erase(it) : ++it;
|
2018-03-12 08:52:32 +01:00
|
|
|
}
|
2018-03-24 11:31:39 +01:00
|
|
|
|
|
|
|
// No matching modes found
|
|
|
|
if (modes.size() == 0)
|
2018-03-12 08:52:32 +01:00
|
|
|
return DXGI_ERROR_NOT_FOUND;
|
|
|
|
|
2018-06-14 11:32:10 +02:00
|
|
|
// If no valid resolution is specified, find the
|
|
|
|
// closest match for the current display resolution
|
|
|
|
UINT targetWidth = pModeToMatch->Width;
|
|
|
|
UINT targetHeight = pModeToMatch->Height;
|
|
|
|
|
|
|
|
if (targetWidth == 0 || targetHeight == 0) {
|
2018-07-14 11:09:02 +02:00
|
|
|
DXGI_MODE_DESC activeMode = { };
|
2018-06-14 11:32:10 +02:00
|
|
|
GetDisplayMode(&activeMode, ENUM_CURRENT_SETTINGS);
|
|
|
|
|
|
|
|
targetWidth = activeMode.Width;
|
|
|
|
targetHeight = activeMode.Height;
|
|
|
|
}
|
|
|
|
|
2018-03-24 11:31:39 +01:00
|
|
|
// Select mode with minimal height+width difference
|
2018-04-02 18:49:19 +02:00
|
|
|
UINT minDifference = std::numeric_limits<unsigned int>::max();
|
2018-03-24 13:42:23 +01:00
|
|
|
|
2018-07-02 09:19:27 +02:00
|
|
|
for (auto mode : modes) {
|
2018-06-14 11:32:10 +02:00
|
|
|
UINT currDifference = std::abs(int(targetWidth - mode.Width))
|
|
|
|
+ std::abs(int(targetHeight - mode.Height));
|
2018-03-12 08:52:32 +01:00
|
|
|
|
2018-04-29 23:03:27 +02:00
|
|
|
if (currDifference <= minDifference) {
|
2018-03-12 08:52:32 +01:00
|
|
|
minDifference = currDifference;
|
|
|
|
*pClosestMatch = mode;
|
|
|
|
}
|
|
|
|
}
|
2018-06-14 11:32:10 +02:00
|
|
|
|
2018-03-12 08:52:32 +01: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;
|
|
|
|
|
2018-04-12 20:12:48 +02:00
|
|
|
::MONITORINFOEXW monInfo;
|
2018-01-18 23:59:40 +01:00
|
|
|
monInfo.cbSize = sizeof(monInfo);
|
2018-03-24 13:42:23 +01:00
|
|
|
|
2018-04-12 20:12:48 +02:00
|
|
|
if (!::GetMonitorInfoW(m_monitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: Failed to query monitor info");
|
2018-01-13 16:32:46 +01:00
|
|
|
return E_FAIL;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
2018-04-12 20:12:48 +02:00
|
|
|
std::memcpy(pDesc->DeviceName, monInfo.szDevice, std::size(pDesc->DeviceName));
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
pDesc->DesktopCoordinates = monInfo.rcMonitor;
|
2017-10-11 03:09:04 +02:00
|
|
|
pDesc->AttachedToDesktop = 1;
|
|
|
|
pDesc->Rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
|
2018-01-13 16:32:46 +01:00
|
|
|
pDesc->Monitor = m_monitor;
|
2017-10-11 03:09:04 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
modes.resize(*pNumModes);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
// Query monitor info to get the device name
|
2018-04-12 20:12:48 +02:00
|
|
|
::MONITORINFOEXW monInfo;
|
2018-01-18 23:59:40 +01:00
|
|
|
monInfo.cbSize = sizeof(monInfo);
|
2018-03-24 13:42:23 +01:00
|
|
|
|
2018-04-12 20:12:48 +02:00
|
|
|
if (!::GetMonitorInfoW(m_monitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: Failed to query monitor info");
|
2018-01-13 16:32:46 +01:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
2017-10-11 03:09:04 +02:00
|
|
|
|
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.
|
2018-04-12 20:12:48 +02:00
|
|
|
DEVMODEW 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
|
|
|
|
2018-04-12 20:12:48 +02:00
|
|
|
while (::EnumDisplaySettingsW(monInfo.szDevice, srcModeId++, &devMode)) {
|
2018-01-13 16:32:46 +01:00
|
|
|
// Skip interlaced modes altogether
|
|
|
|
if (devMode.dmDisplayFlags & DM_INTERLACED)
|
|
|
|
continue;
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
// Skip modes with incompatible formats
|
|
|
|
if (devMode.dmBitsPerPel != GetFormatBpp(EnumFormat))
|
|
|
|
continue;
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
if (pDesc != nullptr) {
|
2018-10-11 09:39:24 +02:00
|
|
|
DXGI_MODE_DESC1 mode;
|
2018-01-13 16:32:46 +01:00
|
|
|
mode.Width = devMode.dmPelsWidth;
|
|
|
|
mode.Height = devMode.dmPelsHeight;
|
2018-05-04 17:37:29 +02:00
|
|
|
mode.RefreshRate = { devMode.dmDisplayFrequency * 1000, 1000 };
|
2018-01-13 16:32:46 +01:00
|
|
|
mode.Format = EnumFormat;
|
|
|
|
mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
|
2018-04-18 16:04:17 +02:00
|
|
|
mode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
2018-10-11 09:39:24 +02:00
|
|
|
mode.Stereo = FALSE;
|
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) {
|
|
|
|
DXGI_VK_OUTPUT_DATA outputData;
|
|
|
|
|
|
|
|
if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: Failed to query output data");
|
2018-04-12 13:38:22 +02:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pStats = outputData.FrameStats;
|
|
|
|
return S_OK;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-12 13:38:22 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL* pArray) {
|
|
|
|
DXGI_VK_OUTPUT_DATA outputData;
|
|
|
|
|
|
|
|
if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: Failed to query output data");
|
2018-04-12 13:38:22 +02:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pArray = outputData.GammaCurve;
|
|
|
|
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) {
|
|
|
|
DXGI_VK_OUTPUT_DATA outputData;
|
|
|
|
|
|
|
|
if (FAILED(m_adapter->GetOutputData(m_monitor, &outputData))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: Failed to query output data");
|
2018-04-12 13:38:22 +02:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
outputData.GammaCurve = *pArray;
|
|
|
|
outputData.GammaDirty = TRUE;
|
|
|
|
|
|
|
|
if (FAILED(m_adapter->SetOutputData(m_monitor, &outputData))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: Failed to update output data");
|
2018-04-12 13:38:22 +02:00
|
|
|
return E_FAIL;
|
|
|
|
} return S_OK;
|
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))
|
2018-10-09 14:48:49 +02:00
|
|
|
Logger::warn("DxgiOutput::WaitForVBlank: Stub");
|
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) {
|
|
|
|
static bool s_errorShown = false;
|
|
|
|
|
|
|
|
if (!std::exchange(s_errorShown, true))
|
|
|
|
Logger::warn("DxgiOutput::DuplicateOutput: Stub");
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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-04-29 23:03:27 +02:00
|
|
|
HRESULT DxgiOutput::GetDisplayMode(DXGI_MODE_DESC* pMode, DWORD ModeNum) {
|
|
|
|
::MONITORINFOEXW monInfo;
|
|
|
|
monInfo.cbSize = sizeof(monInfo);
|
|
|
|
|
|
|
|
if (!::GetMonitorInfoW(m_monitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
|
|
|
|
Logger::err("DXGI: Failed to query monitor info");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEVMODEW devMode = { };
|
|
|
|
devMode.dmSize = sizeof(devMode);
|
|
|
|
|
|
|
|
if (!::EnumDisplaySettingsW(monInfo.szDevice, ModeNum, &devMode))
|
|
|
|
return DXGI_ERROR_NOT_FOUND;
|
|
|
|
|
|
|
|
pMode->Width = devMode.dmPelsWidth;
|
|
|
|
pMode->Height = devMode.dmPelsHeight;
|
|
|
|
pMode->RefreshRate = { devMode.dmDisplayFrequency, 1 };
|
|
|
|
pMode->Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; // FIXME
|
|
|
|
pMode->ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
|
|
|
|
pMode->Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiOutput::SetDisplayMode(const DXGI_MODE_DESC* pMode) {
|
|
|
|
::MONITORINFOEXW monInfo;
|
|
|
|
monInfo.cbSize = sizeof(monInfo);
|
|
|
|
|
|
|
|
if (!::GetMonitorInfoW(m_monitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
|
|
|
|
Logger::err("DXGI: Failed to query monitor info");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEVMODEW devMode = { };
|
|
|
|
devMode.dmSize = sizeof(devMode);
|
|
|
|
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
|
|
|
|
devMode.dmPelsWidth = pMode->Width;
|
|
|
|
devMode.dmPelsHeight = pMode->Height;
|
|
|
|
devMode.dmBitsPerPel = GetFormatBpp(pMode->Format);
|
|
|
|
|
|
|
|
if (pMode->RefreshRate.Numerator != 0) {
|
|
|
|
devMode.dmFields |= DM_DISPLAYFREQUENCY;
|
|
|
|
devMode.dmDisplayFrequency = pMode->RefreshRate.Numerator
|
|
|
|
/ pMode->RefreshRate.Denominator;
|
|
|
|
}
|
|
|
|
|
2018-08-26 12:34:23 +02:00
|
|
|
Logger::info(str::format("DXGI: Setting display mode: ",
|
|
|
|
devMode.dmPelsWidth, "x", devMode.dmPelsHeight, "@",
|
|
|
|
devMode.dmDisplayFrequency));
|
|
|
|
|
2018-04-29 23:03:27 +02:00
|
|
|
LONG status = ::ChangeDisplaySettingsExW(
|
|
|
|
monInfo.szDevice, &devMode, nullptr, CDS_FULLSCREEN, nullptr);
|
|
|
|
|
|
|
|
return status == DISP_CHANGE_SUCCESSFUL ? S_OK : DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
uint32_t DxgiOutput::GetFormatBpp(DXGI_FORMAT Format) const {
|
2018-04-12 17:49:14 +02:00
|
|
|
DXGI_VK_FORMAT_INFO formatInfo = m_adapter->LookupFormat(Format, DXGI_VK_FORMAT_MODE_ANY);
|
|
|
|
return imageFormatInfo(formatInfo.Format)->elementSize * 8;
|
2018-01-13 16:32:46 +01:00
|
|
|
}
|
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|