diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index eda66d95e..be41ad228 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -392,6 +392,8 @@ bool GPUDevice::Init() LOG(Info, "Total graphics memory: {0}", Utilities::BytesToText(TotalGraphicsMemory)); if (!Limits.HasCompute) LOG(Warning, "Compute Shaders are not supported"); + for (const auto& videoOutput : VideoOutputs) + LOG(Info, "Video output '{0}' {1}x{2} {3} Hz", videoOutput.Name, videoOutput.Width, videoOutput.Height, videoOutput.RefreshRate); Engine::RequestingExit.Bind(this); return false; } @@ -725,6 +727,7 @@ void GPUDevice::Draw() void GPUDevice::Dispose() { RenderList::CleanupCache(); + VideoOutputs.Resize(0); VideoOutputModes.Resize(0); } diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h index 8914085eb..8f9393a5b 100644 --- a/Source/Engine/Graphics/GPUDevice.h +++ b/Source/Engine/Graphics/GPUDevice.h @@ -54,7 +54,40 @@ public: }; /// - /// Describes a video output display mode. + /// Describes a video output display (monitor). + /// + API_STRUCT() struct VideoOutput + { + DECLARE_SCRIPTING_TYPE_NO_SPAWN(VideoOutputMode); + + /// + /// The display name. + /// + API_FIELD() String Name; + + /// + /// The native screen resolution width (in pixel). + /// + API_FIELD() uint32 Width = 0; + + /// + /// The native screen resolution height (in pixel). + /// + API_FIELD() uint32 Height = 0; + + /// + /// The maximum screen refresh rate (in hertz). + /// + API_FIELD() float RefreshRate = 0; + + /// + /// Flag that indicates that monitor supports displaying High Dynamic Range colors. + /// + API_FIELD() bool HDR = false; + }; + + /// + /// Describes a video output display mode (monitor screen mode). /// API_STRUCT() struct VideoOutputMode { @@ -73,7 +106,12 @@ public: /// /// The screen refresh rate (in hertz). /// - API_FIELD() uint32 RefreshRate; + API_FIELD() float RefreshRate; + + /// + /// The index of the VideoOutput from the device monitors list. + /// + API_FIELD() int32 VideoOutputIndex; }; /// @@ -134,6 +172,11 @@ public: /// API_FIELD(ReadOnly) GPULimits Limits; + /// + /// The available video outputs (monitors). + /// + API_FIELD(ReadOnly) Array VideoOutputs; + /// /// The available video output modes. /// diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp index 811076af6..0d9ff88d4 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp @@ -136,7 +136,11 @@ bool GPUBufferDX12::OnInit() // Create resource ID3D12Resource* resource; +#if PLATFORM_WINDOWS D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; +#else + D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE; +#endif D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON; VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateCommittedResource(&heapProperties, heapFlags, &resourceDesc, initialState, nullptr, IID_PPV_ARGS(&resource))); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index e140da37e..88334be60 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -307,6 +307,32 @@ bool GPUDeviceDX12::Init() LOG(Info, "Hardware Version: {0}", hwVer); updateFrameEvents(); + // Setup display output + auto& videoOutput = VideoOutputs.AddOne(); + videoOutput.Name = hwVer; + ComPtr dxgiDevice; + VALIDATE_DIRECTX_CALL(_device->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxgiDevice))); + ComPtr dxgiAdapter; + VALIDATE_DIRECTX_CALL(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf())); + ComPtr dxgiOutput; + VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); + DXGI_FORMAT backbufferFormat = RenderToolsDX::ToDxgiFormat(GPU_BACK_BUFFER_PIXEL_FORMAT); + UINT modesCount = 0; + VALIDATE_DIRECTX_CALL(dxgiOutput->GetDisplayModeList(backbufferFormat, 0, &modesCount, NULL)); + Array modes; + modes.Resize((int32)modesCount); + VALIDATE_DIRECTX_CALL(dxgiOutput->GetDisplayModeListX(backbufferFormat, 0, &modesCount, modes.Get())); + for (const DXGIXBOX_MODE_DESC& mode : modes) + { + if (mode.Width > videoOutput.Width) + { + videoOutput.Width = mode.Width; + videoOutput.Height = mode.Height; + } + videoOutput.RefreshRate = Math::Max(videoOutput.RefreshRate, mode.RefreshRate.Numerator / (float)mode.RefreshRate.Denominator); + } + modes.Resize(0); + #if PLATFORM_GDK GDKPlatform::Suspended.Bind(this); GDKPlatform::Resumed.Bind(this); @@ -943,6 +969,7 @@ void GPUDeviceDX12::updateFrameEvents() dxgiAdapter->GetDesc(&_adapter->Description); ComPtr dxgiOutput; VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); + // TODO: support 120/40/30/24 fps VALIDATE_DIRECTX_CALL(_device->SetFrameIntervalX(dxgiOutput.Get(), D3D12XBOX_FRAME_INTERVAL_60_HZ, DX12_BACK_BUFFER_COUNT - 1u, D3D12XBOX_FRAME_INTERVAL_FLAG_NONE)); VALIDATE_DIRECTX_CALL(_device->ScheduleFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, 0U, nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index abecfd776..e7e4c5ca2 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -159,7 +159,11 @@ bool GPUTextureDX12::OnInit() initialState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; // Create texture +#if PLATFORM_WINDOWS D3D12_HEAP_FLAGS heapFlags = useRTV || useDSV ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE; +#else + D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE; +#endif auto result = device->CreateCommittedResource(&heapProperties, heapFlags, &resourceDesc, initialState, clearValuePtr, IID_PPV_ARGS(&resource)); LOG_DIRECTX_RESULT_WITH_RETURN(result, true); diff --git a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp index f195b8b41..7e38ecadb 100644 --- a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp @@ -7,6 +7,7 @@ #include "GPUDeviceDX.h" #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/StringBuilder.h" +#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Graphics/GPUDevice.h" #include "IncludeDirectXHeaders.h" #include @@ -21,6 +22,9 @@ typedef void* LPCDLGTEMPLATE; #pragma comment(lib, "SetupAPI.lib") #endif +#define DISPLAY_DEVICE_ACTIVE 0x00000001 +#define DISPLAY_DEVICE_MIRRORING_DRIVER 0x00000008 + namespace Windows { typedef struct _devicemodeW @@ -84,7 +88,18 @@ namespace Windows DWORD dmPanningHeight; } DEVMODEW, *PDEVMODEW, *NPDEVMODEW, *LPDEVMODEW; + typedef struct _DISPLAY_DEVICEW + { + DWORD cb; + WCHAR DeviceName[32]; + WCHAR DeviceString[128]; + DWORD StateFlags; + WCHAR DeviceID[128]; + WCHAR DeviceKey[128]; + } DISPLAY_DEVICEW, *PDISPLAY_DEVICEW, *LPDISPLAY_DEVICEW; + WIN_API BOOL WIN_API_CALLCONV EnumDisplaySettingsW(LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode); + WIN_API BOOL WIN_API_CALLCONV EnumDisplayDevicesW(LPCWSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICEW lpDisplayDevice, DWORD dwFlags); } // @formatter:off @@ -590,13 +605,14 @@ void GPUAdapterDX::SetDriverVersion(Version& ver) DriverVersion = ver; } +#if PLATFORM_WINDOWS + void GPUDeviceDX::UpdateOutputs(IDXGIAdapter* adapter) { -#if PLATFORM_WINDOWS - // Collect output devices + PROFILE_CPU(); uint32 outputIdx = 0; ComPtr output; - DXGI_FORMAT defaultBackbufferFormat = RenderToolsDX::ToDxgiFormat(GPU_BACK_BUFFER_PIXEL_FORMAT); + DXGI_FORMAT backbufferFormat = RenderToolsDX::ToDxgiFormat(GPU_BACK_BUFFER_PIXEL_FORMAT); Array modeDesc; while (adapter->EnumOutputs(outputIdx, &output) != DXGI_ERROR_NOT_FOUND) { @@ -606,7 +622,7 @@ void GPUDeviceDX::UpdateOutputs(IDXGIAdapter* adapter) output->GetDesc(&outputDX11.Desc); uint32 numModes = 0; - HRESULT hr = output->GetDisplayModeList(defaultBackbufferFormat, 0, &numModes, nullptr); + HRESULT hr = output->GetDisplayModeList(backbufferFormat, 0, &numModes, nullptr); if (FAILED(hr)) { LOG(Warning, "Error while enumerating adapter output video modes."); @@ -614,7 +630,7 @@ void GPUDeviceDX::UpdateOutputs(IDXGIAdapter* adapter) } modeDesc.Resize(numModes, false); - hr = output->GetDisplayModeList(defaultBackbufferFormat, 0, &numModes, modeDesc.Get()); + hr = output->GetDisplayModeList(backbufferFormat, 0, &numModes, modeDesc.Get()); if (FAILED(hr)) { LOG(Warning, "Error while enumerating adapter output video modes."); @@ -635,16 +651,10 @@ void GPUDeviceDX::UpdateOutputs(IDXGIAdapter* adapter) break; } } - if (!foundVideoMode) { outputDX11.VideoModes.Add(mode); - - // Collect only from the main monitor - if (Outputs.Count() == 1) - { - VideoOutputModes.Add({ mode.Width, mode.Height, (uint32)(mode.RefreshRate.Numerator / (float)mode.RefreshRate.Denominator) }); - } + VideoOutputModes.Add({ mode.Width, mode.Height, mode.RefreshRate.Numerator / (float)mode.RefreshRate.Denominator, VideoOutputs.Count() }); } } @@ -659,24 +669,57 @@ void GPUDeviceDX::UpdateOutputs(IDXGIAdapter* adapter) devMode.dmDriverExtra = 0; Windows::EnumDisplaySettingsW(monitorInfo.szDevice, ((DWORD)-1), &devMode); + // Initialize display mode for Desktop DXGI_MODE_DESC currentMode; currentMode.Width = devMode.dmPelsWidth; currentMode.Height = devMode.dmPelsHeight; bool useDefaultRefreshRate = 1 == devMode.dmDisplayFrequency || 0 == devMode.dmDisplayFrequency; currentMode.RefreshRate.Numerator = useDefaultRefreshRate ? 0 : devMode.dmDisplayFrequency; currentMode.RefreshRate.Denominator = useDefaultRefreshRate ? 0 : 1; - currentMode.Format = defaultBackbufferFormat; + currentMode.Format = backbufferFormat; currentMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; currentMode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; - if (output->FindClosestMatchingMode(¤tMode, &outputDX11.DesktopViewMode, nullptr) != S_OK) outputDX11.DesktopViewMode = currentMode; - float refreshRate = outputDX11.DesktopViewMode.RefreshRate.Numerator / (float)outputDX11.DesktopViewMode.RefreshRate.Denominator; - LOG(Info, "Video output '{0}' {1}x{2} {3} Hz", outputDX11.Desc.DeviceName, devMode.dmPelsWidth, devMode.dmPelsHeight, refreshRate); + // Add video output + auto& videoOutput = VideoOutputs.AddOne(); + videoOutput.Width = devMode.dmPelsWidth; + videoOutput.Height = devMode.dmPelsHeight; + videoOutput.RefreshRate = outputDX11.DesktopViewMode.RefreshRate.Numerator / (float)outputDX11.DesktopViewMode.RefreshRate.Denominator; + + // Query display device name + Windows::DISPLAY_DEVICEW displayDevice; + displayDevice.cb = sizeof(displayDevice); + DWORD monitorIndex = 0; + while (Windows::EnumDisplayDevicesW(outputDX11.Desc.DeviceName, monitorIndex, &displayDevice, 0)) + { + if (displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE && !(displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) + { + StringView id(displayDevice.DeviceID); + videoOutput.Name = id.Substring(8, id.Substring(9).Find(TEXT('\\')) + 1); + break; + } + monitorIndex++; + } + if (videoOutput.Name.IsEmpty()) + videoOutput.Name = outputDX11.Desc.DeviceName; + +#ifdef __IDXGIOutput6_INTERFACE_DEFINED__ + // Query HDR support + ComPtr output6; + if (SUCCEEDED(output->QueryInterface(IID_PPV_ARGS(&output6)))) + { + DXGI_OUTPUT_DESC1 outputDesc; + output6->GetDesc1(&outputDesc); + videoOutput.HDR = outputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + } +#endif + outputIdx++; } -#endif } #endif + +#endif