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-01-13 16:32:46 +01:00
|
|
|
#include "../dxvk/dxvk_format.h"
|
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
namespace dxvk {
|
|
|
|
|
|
|
|
DxgiOutput::DxgiOutput(
|
|
|
|
DxgiAdapter* adapter,
|
2018-01-13 16:32:46 +01:00
|
|
|
HMONITOR monitor)
|
|
|
|
: m_adapter(adapter),
|
|
|
|
m_monitor(monitor) {
|
2017-11-26 14:01:41 +01:00
|
|
|
|
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) {
|
2017-10-15 21:50:45 +02:00
|
|
|
COM_QUERY_IFACE(riid, ppvObject, IUnknown);
|
|
|
|
COM_QUERY_IFACE(riid, ppvObject, IDXGIObject);
|
2017-10-11 03:09:04 +02:00
|
|
|
COM_QUERY_IFACE(riid, ppvObject, IDXGIOutput);
|
|
|
|
|
|
|
|
Logger::warn("DxgiOutput::QueryInterface: Unknown interface query");
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetParent(
|
2017-10-11 03:09:04 +02:00
|
|
|
REFIID riid,
|
|
|
|
void **ppParent) {
|
|
|
|
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-03-12 10:52:32 +03:00
|
|
|
if (pModeToMatch == nullptr) {
|
|
|
|
Logger::err("DxgiOutput::FindClosestMatchingMode: pModeToMatch is nullptr");
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pClosestMatch == nullptr) {
|
|
|
|
Logger::err("DxgiOutput::FindClosestMatchingMode: pClosestMatch is nullptr");
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pModeToMatch->Format == DXGI_FORMAT_UNKNOWN && pConcernedDevice == nullptr) {
|
|
|
|
Logger::err("DxgiOutput::FindClosestMatchingMode: no pointer to device was provided for DXGI_FORMAT_UNKNOWN format");
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pModeToMatch->Format == DXGI_FORMAT_UNKNOWN) {
|
|
|
|
/* TODO: perform additional format matching
|
|
|
|
https://msdn.microsoft.com/en-us/library/windows/desktop/bb174547(v=vs.85).aspx?f=255&MSPPError=-2147217396
|
|
|
|
> If pConcernedDevice is NULL, Format CANNOT be DXGI_FORMAT_UNKNOWN.
|
|
|
|
and vice versa
|
|
|
|
>If pConcernedDevice is NOT NULL, Format COULD be DXGI_FORMAT_UNKNOWN.
|
|
|
|
|
|
|
|
But Format in structures from GetDisplayModeList() cannot be
|
|
|
|
DXGI_FORMAT_UNKNOWN by definition.
|
|
|
|
|
|
|
|
There is way in case of DXGI_FORMAT_UNKNOWN and pDevice != nullptr we
|
|
|
|
should perform additional format matching. It may be just ignoring of
|
|
|
|
Format field or using some range of formats but MSDN nothing says
|
|
|
|
about of criteria.
|
|
|
|
*/
|
|
|
|
Logger::err("DxgiOutput::FindClosestMatchingMode: matching formats to device currently is not supported");
|
|
|
|
return DXGI_ERROR_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
DXGI_MODE_DESC modeToMatch = *pModeToMatch;
|
|
|
|
UINT modesCount = 0;
|
|
|
|
GetDisplayModeList(pModeToMatch->Format, 0, &modesCount, nullptr);
|
|
|
|
|
|
|
|
if (modesCount == 0) {
|
|
|
|
Logger::err("DxgiOutput::FindClosestMatchingMode: no device formats were found");
|
|
|
|
return DXGI_ERROR_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<DXGI_MODE_DESC> modes(modesCount);
|
|
|
|
GetDisplayModeList(pModeToMatch->Format, 0, &modesCount, modes.data());
|
|
|
|
|
|
|
|
//filter out modes with different scanline ordering if it was set
|
|
|
|
if (modeToMatch.ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED) {
|
|
|
|
for (auto it = modes.begin(); it != modes.end();) {
|
|
|
|
if (it->ScanlineOrdering != modeToMatch.ScanlineOrdering)
|
|
|
|
it = modes.erase(it);
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//filter out modes with different refresh rate if it was set
|
|
|
|
if (modeToMatch.RefreshRate.Denominator != 0 || modeToMatch.RefreshRate.Numerator != 0) {
|
|
|
|
for (auto it = modes.begin(); it != modes.end();) {
|
|
|
|
if (it->RefreshRate.Denominator != modeToMatch.RefreshRate.Denominator ||
|
|
|
|
it->RefreshRate.Numerator != modeToMatch.RefreshRate.Numerator)
|
|
|
|
it = modes.erase(it);
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return error when there is no modes with target refresh rate and scanline order
|
|
|
|
if(modes.size() == 0) {
|
|
|
|
Logger::err("DxgiOutput::FindClosestMatchingMode: no matched formats were found");
|
|
|
|
return DXGI_ERROR_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
//select mode with minimal height+width difference
|
|
|
|
UINT minDifference = UINT_MAX;
|
|
|
|
for (auto& mode : modes) {
|
|
|
|
UINT currDifference = abs((int)(modeToMatch.Width - mode.Width))
|
|
|
|
+ abs((int)(modeToMatch.Height - mode.Height));
|
|
|
|
|
|
|
|
if (currDifference < minDifference) {
|
|
|
|
minDifference = currDifference;
|
|
|
|
*pClosestMatch = mode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
2017-10-11 03:09:04 +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-01-19 00:59:40 +02:00
|
|
|
::MONITORINFOEX monInfo;
|
|
|
|
|
|
|
|
monInfo.cbSize = sizeof(monInfo);
|
2018-01-13 16:32:46 +01:00
|
|
|
if (!::GetMonitorInfo(m_monitor, &monInfo)) {
|
|
|
|
Logger::err("DxgiOutput: Failed to query monitor info");
|
|
|
|
return E_FAIL;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::memset(pDesc->DeviceName, 0, sizeof(pDesc->DeviceName));
|
2018-01-13 16:32:46 +01:00
|
|
|
std::mbstowcs(pDesc->DeviceName, monInfo.szDevice, _countof(pDesc->DeviceName) - 1);
|
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,
|
|
|
|
UINT *pNumModes,
|
|
|
|
DXGI_MODE_DESC *pDesc) {
|
|
|
|
if (pNumModes == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
2018-03-12 10:52:32 +03:00
|
|
|
|
|
|
|
if (Flags != 0)
|
|
|
|
Logger::warn("DxgiOutput::GetDisplayModeList: flags are ignored");
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
// Query monitor info to get the device name
|
2018-01-19 00:59:40 +02:00
|
|
|
::MONITORINFOEX monInfo;
|
|
|
|
|
|
|
|
monInfo.cbSize = sizeof(monInfo);
|
2018-01-13 16:32:46 +01:00
|
|
|
if (!::GetMonitorInfo(m_monitor, &monInfo)) {
|
|
|
|
Logger::err("DxgiOutput: Failed to query monitor info");
|
|
|
|
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.
|
|
|
|
DEVMODE 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-01-13 16:32:46 +01:00
|
|
|
while (::EnumDisplaySettings(monInfo.szDevice, srcModeId++, &devMode)) {
|
|
|
|
// 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
|
|
|
// Write back display mode
|
|
|
|
if (pDesc != nullptr) {
|
|
|
|
if (dstModeId >= *pNumModes)
|
|
|
|
return DXGI_ERROR_MORE_DATA;
|
2017-10-11 03:09:04 +02:00
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
DXGI_MODE_DESC mode;
|
2018-01-13 16:32:46 +01:00
|
|
|
mode.Width = devMode.dmPelsWidth;
|
|
|
|
mode.Height = devMode.dmPelsHeight;
|
|
|
|
mode.RefreshRate = { devMode.dmDisplayFrequency, 1 };
|
|
|
|
mode.Format = EnumFormat;
|
|
|
|
mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
|
|
|
|
mode.Scaling = DXGI_MODE_SCALING_CENTERED;
|
|
|
|
pDesc[dstModeId] = 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-01-13 16:32:46 +01:00
|
|
|
*pNumModes = dstModeId;
|
|
|
|
return S_OK;
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData(IDXGISurface *pDestination) {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::err("DxgiOutput::GetDisplaySurfaceData: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetFrameStatistics(DXGI_FRAME_STATISTICS *pStats) {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::err("DxgiOutput::GetFrameStatistics: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL *pArray) {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::err("DxgiOutput::GetGammaControl: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControlCapabilities(DXGI_GAMMA_CONTROL_CAPABILITIES *pGammaCaps) {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::err("DxgiOutput::GetGammaControlCapabilities: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::SetDisplaySurface(IDXGISurface *pScanoutSurface) {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::err("DxgiOutput::SetDisplaySurface: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiOutput::SetGammaControl(const DXGI_GAMMA_CONTROL *pArray) {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::err("DxgiOutput::SetGammaControl: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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() {
|
2017-10-11 03:09:04 +02:00
|
|
|
Logger::warn("DxgiOutput::WaitForVBlank: Stub");
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2018-01-13 16:32:46 +01:00
|
|
|
|
|
|
|
uint32_t DxgiOutput::GetFormatBpp(DXGI_FORMAT Format) const {
|
|
|
|
DxgiFormatInfo formatInfo = m_adapter->LookupFormat(Format, DxgiFormatMode::Any);
|
|
|
|
return imageFormatInfo(formatInfo.format)->elementSize * 8;
|
|
|
|
}
|
|
|
|
|
2017-10-11 03:09:04 +02:00
|
|
|
}
|