Browse Source

Extract copy-paste from Graphics into common place.

This commit shall not have any functional changes except the following:

- Semantics of multi sample level on DX9 backend has changed.
It represents actual hardware more instead of caller's desire.
Behavior of DX9, DX11 and OpenGL backend is synchronized now.
Eugene Kozlov 5 years ago
parent
commit
27216959ee

+ 34 - 132
Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.cpp

@@ -269,93 +269,32 @@ Graphics::~Graphics()
     context_->ReleaseSDL();
 }
 
-bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable, bool highDPI, bool vsync, bool tripleBuffer,
-    int multiSample, int monitor, int refreshRate)
+bool Graphics::SetScreenMode(int width, int height, const ScreenModeParams& params, bool maximize)
 {
     URHO3D_PROFILE(SetScreenMode);
 
-    highDPI = false;   // SDL does not support High DPI mode on Windows platform yet, so always disable it for now
-
-    bool maximize = false;
-
-    // Make sure monitor index is not bigger than the currently detected monitors
-    int monitors = SDL_GetNumVideoDisplays();
-    if (monitor >= monitors || monitor < 0)
-        monitor = 0; // this monitor is not present, use first monitor
+    // Ensure that parameters are properly filled
+    ScreenModeParams newParams = params;
+    AdjustScreenMode(width, height, newParams, maximize);
 
     // Find out the full screen mode display format (match desktop color depth)
     SDL_DisplayMode mode;
-    SDL_GetDesktopDisplayMode(monitor, &mode);
-    DXGI_FORMAT fullscreenFormat = SDL_BITSPERPIXEL(mode.format) == 16 ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM;
-
-    // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size. If zero in fullscreen, use desktop mode
-    if (!width || !height)
-    {
-        if (fullscreen || borderless)
-        {
-            width = mode.w;
-            height = mode.h;
-        }
-        else
-        {
-            maximize = resizable;
-            width = 1024;
-            height = 768;
-        }
-    }
-
-    // Fullscreen or Borderless can not be resizable
-    if (fullscreen || borderless)
-        resizable = false;
-
-    // Borderless cannot be fullscreen, they are mutually exclusive
-    if (borderless)
-        fullscreen = false;
+    SDL_GetDesktopDisplayMode(newParams.monitor_, &mode);
+    const DXGI_FORMAT fullscreenFormat = SDL_BITSPERPIXEL(mode.format) == 16 ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM;
 
     // If nothing changes, do not reset the device
-    if (width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ &&
-        resizable == resizable_ && vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_ &&
-        monitor == monitor_ && refreshRate == refreshRate_)
+    if (width == width_ && height == height_ && newParams == screenParams_)
         return true;
 
     SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
 
     if (!window_)
     {
-        if (!OpenWindow(width, height, resizable, borderless))
+        if (!OpenWindow(width, height, newParams.resizable_, newParams.borderless_))
             return false;
     }
 
-    // Check fullscreen mode validity. Use a closest match if not found
-    if (fullscreen)
-    {
-        PODVector<IntVector3> resolutions = GetResolutions(monitor);
-        if (resolutions.Size())
-        {
-            unsigned best = 0;
-            unsigned bestError = M_MAX_UNSIGNED;
-
-            for (unsigned i = 0; i < resolutions.Size(); ++i)
-            {
-                unsigned error = (unsigned)(Abs(resolutions[i].x_ - width) + Abs(resolutions[i].y_ - height));
-                if (refreshRate != 0)
-                    error += (unsigned)Abs(resolutions[i].z_ - refreshRate);
-                if (error < bestError)
-                {
-                    best = i;
-                    bestError = error;
-                }
-            }
-
-            width = resolutions[best].x_;
-            height = resolutions[best].y_;
-            refreshRate = resolutions[best].z_;
-        }
-    }
-
-    AdjustWindow(width, height, fullscreen, borderless, monitor);
-    monitor_ = monitor;
-    refreshRate_ = refreshRate;
+    AdjustWindow(width, height, newParams.fullscreen_, newParams.borderless_, newParams.monitor_);
 
     if (maximize)
     {
@@ -363,57 +302,21 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         SDL_GetWindowSize(window_, &width, &height);
     }
 
-    if (!impl_->device_ || multiSample_ != multiSample)
-        CreateDevice(width, height, multiSample);
-    UpdateSwapChain(width, height);
+    const int oldMultiSample = screenParams_.multiSample_;
+    screenParams_ = newParams;
 
-    fullscreen_ = fullscreen;
-    borderless_ = borderless;
-    resizable_ = resizable;
-    highDPI_ = highDPI;
-    vsync_ = vsync;
-    tripleBuffer_ = tripleBuffer;
+    if (!impl_->device_ || screenParams_.multiSample_ != oldMultiSample)
+        CreateDevice(width, height);
+    UpdateSwapChain(width, height);
 
     // Clear the initial window contents to black
     Clear(CLEAR_COLOR);
     impl_->swapChain_->Present(0, 0);
 
-#ifdef URHO3D_LOGGING
-    String msg;
-    msg.AppendWithFormat("Set screen mode %dx%d rate %d Hz %s monitor %d", width_, height_, refreshRate_,
-        (fullscreen_ ? "fullscreen" : "windowed"), monitor_);
-    if (borderless_)
-        msg.Append(" borderless");
-    if (resizable_)
-        msg.Append(" resizable");
-    if (highDPI_)
-        msg.Append(" highDPI");
-    if (multiSample > 1)
-        msg.AppendWithFormat(" multisample %d", multiSample);
-    URHO3D_LOGINFO(msg);
-#endif
-
-    using namespace ScreenMode;
-
-    VariantMap& eventData = GetEventDataMap();
-    eventData[P_WIDTH] = width_;
-    eventData[P_HEIGHT] = height_;
-    eventData[P_FULLSCREEN] = fullscreen_;
-    eventData[P_BORDERLESS] = borderless_;
-    eventData[P_RESIZABLE] = resizable_;
-    eventData[P_HIGHDPI] = highDPI_;
-    eventData[P_MONITOR] = monitor_;
-    eventData[P_REFRESHRATE] = refreshRate_;
-    SendEvent(E_SCREENMODE, eventData);
-
+    OnScreenModeChanged();
     return true;
 }
 
-bool Graphics::SetMode(int width, int height)
-{
-    return SetMode(width, height, fullscreen_, borderless_, resizable_, highDPI_, vsync_, tripleBuffer_, multiSample_, monitor_, refreshRate_);
-}
-
 void Graphics::SetSRGB(bool enable)
 {
     bool newEnable = enable && sRGBWriteSupport_;
@@ -423,7 +326,7 @@ void Graphics::SetSRGB(bool enable)
         if (impl_->swapChain_)
         {
             // Recreate swap chain for the new backbuffer format
-            CreateDevice(width_, height_, multiSample_);
+            CreateDevice(width_, height_);
             UpdateSwapChain(width_, height_);
         }
     }
@@ -496,7 +399,7 @@ bool Graphics::TakeScreenShot(Image& destImage)
     ID3D11Resource* source = nullptr;
     impl_->defaultRenderTargetView_->GetResource(&source);
 
-    if (multiSample_ > 1)
+    if (screenParams_.multiSample_ > 1)
     {
         // If backbuffer is multisampled, need another DEFAULT usage texture to resolve the data to first
         CreateResolveTexture();
@@ -563,7 +466,7 @@ bool Graphics::BeginFrame()
     {
         // To prevent a loop of endless device loss and flicker, do not attempt to render when in fullscreen
         // and the window is minimized
-        if (fullscreen_ && (SDL_GetWindowFlags(window_) & SDL_WINDOW_MINIMIZED))
+        if (screenParams_.fullscreen_ && (SDL_GetWindowFlags(window_) & SDL_WINDOW_MINIMIZED))
             return false;
     }
 
@@ -590,7 +493,7 @@ void Graphics::EndFrame()
         URHO3D_PROFILE(Present);
 
         SendEvent(E_ENDRENDERING);
-        impl_->swapChain_->Present(vsync_ ? 1 : 0, 0);
+        impl_->swapChain_->Present(screenParams_.vsync_ ? 1 : 0, 0);
     }
 
     // Clean up too large scratch buffers
@@ -683,7 +586,7 @@ bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
     srcBox.back = 1;
 
     ID3D11Resource* source = nullptr;
-    bool resolve = multiSample_ > 1;
+    const bool resolve = screenParams_.multiSample_ > 1;
     impl_->defaultRenderTargetView_->GetResource(&source);
 
     if (!resolve)
@@ -1845,16 +1748,16 @@ void Graphics::OnWindowResized()
     VariantMap& eventData = GetEventDataMap();
     eventData[P_WIDTH] = width_;
     eventData[P_HEIGHT] = height_;
-    eventData[P_FULLSCREEN] = fullscreen_;
-    eventData[P_RESIZABLE] = resizable_;
-    eventData[P_BORDERLESS] = borderless_;
-    eventData[P_HIGHDPI] = highDPI_;
+    eventData[P_FULLSCREEN] = screenParams_.fullscreen_;
+    eventData[P_RESIZABLE] = screenParams_.resizable_;
+    eventData[P_BORDERLESS] = screenParams_.borderless_;
+    eventData[P_HIGHDPI] = screenParams_.highDPI_;
     SendEvent(E_SCREENMODE, eventData);
 }
 
 void Graphics::OnWindowMoved()
 {
-    if (!impl_->device_ || !window_ || fullscreen_)
+    if (!impl_->device_ || !window_ || screenParams_.fullscreen_)
         return;
 
     int newX, newY;
@@ -2110,7 +2013,7 @@ void Graphics::AdjustWindow(int& newWidth, int& newHeight, bool& newFullscreen,
     }
 }
 
-bool Graphics::CreateDevice(int width, int height, int multiSample)
+bool Graphics::CreateDevice(int width, int height)
 {
     // Device needs only to be created once
     if (!impl_->device_)
@@ -2143,8 +2046,8 @@ bool Graphics::CreateDevice(int width, int height, int multiSample)
 
     // Check that multisample level is supported
     PODVector<int> multiSampleLevels = GetMultiSampleLevels();
-    if (!multiSampleLevels.Contains(multiSample))
-        multiSample = 1;
+    if (!multiSampleLevels.Contains(screenParams_.multiSample_))
+        screenParams_.multiSample_ = 1;
 
     // Create swap chain. Release old if necessary
     if (impl_->swapChain_)
@@ -2163,7 +2066,7 @@ bool Graphics::CreateDevice(int width, int height, int multiSample)
     DXGI_RATIONAL refreshRateRational = {};
     IDXGIOutput* dxgiOutput = nullptr;
     UINT numModes = 0;
-    dxgiAdapter->EnumOutputs(monitor_, &dxgiOutput);
+    dxgiAdapter->EnumOutputs(screenParams_.monitor_, &dxgiOutput);
     dxgiOutput->GetDisplayModeList(sRGB_ ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM, 0, &numModes, 0);
 
     // find the best matching refresh rate with the specified resolution
@@ -2179,7 +2082,7 @@ bool Graphics::CreateDevice(int width, int height, int multiSample)
                 continue;
 
             float rate = (float)modes[i].RefreshRate.Numerator / modes[i].RefreshRate.Denominator;
-            unsigned error = (unsigned)(Abs(rate - refreshRate_));
+            unsigned error = (unsigned)(Abs(rate - screenParams_.refreshRate_));
             if (error < bestError)
             {
                 bestMatchingRateIndex = i;
@@ -2206,8 +2109,8 @@ bool Graphics::CreateDevice(int width, int height, int multiSample)
     swapChainDesc.BufferDesc.RefreshRate.Numerator = refreshRateRational.Numerator;
     swapChainDesc.BufferDesc.RefreshRate.Denominator = refreshRateRational.Denominator;
     swapChainDesc.OutputWindow = GetWindowHandle(window_);
-    swapChainDesc.SampleDesc.Count = (UINT)multiSample;
-    swapChainDesc.SampleDesc.Quality = impl_->GetMultiSampleQuality(swapChainDesc.BufferDesc.Format, multiSample);
+    swapChainDesc.SampleDesc.Count = static_cast<UINT>(screenParams_.multiSample_);
+    swapChainDesc.SampleDesc.Quality = impl_->GetMultiSampleQuality(swapChainDesc.BufferDesc.Format, screenParams_.multiSample_);
     swapChainDesc.Windowed = TRUE;
     swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
 
@@ -2234,7 +2137,6 @@ bool Graphics::CreateDevice(int width, int height, int multiSample)
         return false;
     }
 
-    multiSample_ = multiSample;
     return true;
 }
 
@@ -2301,8 +2203,8 @@ bool Graphics::UpdateSwapChain(int width, int height)
     depthDesc.MipLevels = 1;
     depthDesc.ArraySize = 1;
     depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
-    depthDesc.SampleDesc.Count = (UINT)multiSample_;
-    depthDesc.SampleDesc.Quality = impl_->GetMultiSampleQuality(depthDesc.Format, multiSample_);
+    depthDesc.SampleDesc.Count = static_cast<UINT>(screenParams_.multiSample_);
+    depthDesc.SampleDesc.Quality = impl_->GetMultiSampleQuality(depthDesc.Format, screenParams_.multiSample_);
     depthDesc.Usage = D3D11_USAGE_DEFAULT;
     depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
     depthDesc.CPUAccessFlags = 0;

+ 34 - 135
Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -300,64 +300,30 @@ Graphics::~Graphics()
     context_->ReleaseSDL();
 }
 
-bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable, bool highDPI, bool vsync,
-    bool tripleBuffer, int multiSample, int monitor, int refreshRate)
+bool Graphics::SetScreenMode(int width, int height, const ScreenModeParams& params, bool maximize)
 {
     URHO3D_PROFILE(SetScreenMode);
-    bool monitorChanged = false;
 
-    highDPI = false;   // SDL does not support High DPI mode on Windows platform yet, so always disable it for now
+    // Ensure that parameters are properly filled
+    ScreenModeParams newParams = params;
+    AdjustScreenMode(width, height, newParams, maximize);
 
-    bool maximize = false;
-
-    // Make sure monitor index is not bigger than the currently detected monitors
-    int monitors = SDL_GetNumVideoDisplays();
-    if (monitor >= monitors || monitor < 0)
-        monitor = 0; // this monitor is not present, use first monitor
+    // If nothing changes, do not reset the device
+    if (width_ == width && height_ == height && screenParams_ == newParams)
+        return true;
 
     // Find out the full screen mode display format (match desktop color depth)
     SDL_DisplayMode mode;
-    SDL_GetDesktopDisplayMode(monitor, &mode);
-    D3DFORMAT fullscreenFormat = SDL_BITSPERPIXEL(mode.format) == 16 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8;
-
-    // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size. If zero in fullscreen, use desktop mode
-    if (!width || !height)
-    {
-        if (fullscreen || borderless)
-        {
-            width = mode.w;
-            height = mode.h;
-        }
-        else
-        {
-            maximize = resizable;
-            width = 1024;
-            height = 768;
-        }
-    }
-
-    // Fullscreen or Borderless can not be resizable
-    if (fullscreen || borderless)
-        resizable = false;
-
-    // Borderless cannot be fullscreen, they are mutually exclusive
-    if (borderless)
-        fullscreen = false;
-
-    multiSample = Clamp(multiSample, 1, (int)D3DMULTISAMPLE_16_SAMPLES);
+    SDL_GetDesktopDisplayMode(newParams.monitor_, &mode);
+    const D3DFORMAT fullscreenFormat = SDL_BITSPERPIXEL(mode.format) == 16 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8;
 
-    // If nothing changes, do not reset the device
-    if (width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ && resizable == resizable_ &&
-        vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_ && monitor == monitor_ && refreshRate == refreshRate_)
-        return true;
-
-    monitorChanged = monitor != monitor_;
+    const bool monitorChanged = screenParams_.monitor_ != newParams.monitor_;
 
     SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
 
     if (!window_)
     {
-        if (!OpenWindow(width, height, resizable, borderless))
+        if (!OpenWindow(width, height, newParams.resizable_, newParams.borderless_))
             return false;
     }
 
@@ -369,44 +335,14 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         CheckFeatureSupport();
     }
 
-    // Note: GetMultiSample() will not reflect the actual hardware multisample mode, but rather what the caller wanted.
-    multiSample_ = multiSample;
-
-    // Check fullscreen mode validity. Use a closest match if not found
-    if (fullscreen)
-    {
-        PODVector<IntVector3> resolutions = GetResolutions(monitor);
-        if (resolutions.Size())
-        {
-            unsigned best = 0;
-            unsigned bestError = M_MAX_UNSIGNED;
-
-            for (unsigned i = 0; i < resolutions.Size(); ++i)
-            {
-                unsigned error = (unsigned)(Abs(resolutions[i].x_ - width) + Abs(resolutions[i].y_ - height));
-                if (refreshRate != 0)
-                    error += (unsigned)Abs(resolutions[i].z_ - refreshRate);
-                if (error < bestError)
-                {
-                    best = i;
-                    bestError = error;
-                }
-            }
-
-            width = resolutions[best].x_;
-            height = resolutions[best].y_;
-            refreshRate = resolutions[best].z_;
-        }
-    }
-
     // Fall back to non-multisampled if unsupported multisampling mode
-    if (multiSample > 1)
+    if (newParams.multiSample_ > 1)
     {
-        if (!impl_->CheckMultiSampleSupport(fullscreenFormat, multiSample))
-            multiSample = 1;
+        if (!impl_->CheckMultiSampleSupport(fullscreenFormat, newParams.multiSample_))
+            newParams.multiSample_ = 1;
     }
 
-    AdjustWindow(width, height, fullscreen, borderless, monitor);
+    AdjustWindow(width, height, newParams.fullscreen_, newParams.borderless_, newParams.monitor_);
 
     if (maximize)
     {
@@ -414,7 +350,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         SDL_GetWindowSize(window_, &width, &height);
     }
 
-    if (fullscreen)
+    if (newParams.fullscreen_)
     {
         impl_->presentParams_.BackBufferFormat = fullscreenFormat;
         impl_->presentParams_.Windowed = false;
@@ -427,35 +363,28 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
 
     impl_->presentParams_.BackBufferWidth = (UINT)width;
     impl_->presentParams_.BackBufferHeight = (UINT)height;
-    impl_->presentParams_.BackBufferCount = tripleBuffer ? 2 : 1;
-    impl_->presentParams_.MultiSampleType = multiSample > 1 ? (D3DMULTISAMPLE_TYPE)multiSample : D3DMULTISAMPLE_NONE;
+    impl_->presentParams_.BackBufferCount = newParams.tripleBuffer_ ? 2 : 1;
+    impl_->presentParams_.MultiSampleType = newParams.multiSample_ > 1 ? static_cast<D3DMULTISAMPLE_TYPE>(newParams.multiSample_) : D3DMULTISAMPLE_NONE;
     impl_->presentParams_.MultiSampleQuality = 0;
     impl_->presentParams_.SwapEffect = D3DSWAPEFFECT_DISCARD;
     impl_->presentParams_.hDeviceWindow = GetWindowHandle(window_);
     impl_->presentParams_.EnableAutoDepthStencil = TRUE;
     impl_->presentParams_.AutoDepthStencilFormat = D3DFMT_D24S8;
     impl_->presentParams_.Flags = D3DPRESENT_LINEAR_CONTENT;
-    impl_->presentParams_.FullScreen_RefreshRateInHz = fullscreen ? refreshRate : D3DPRESENT_RATE_DEFAULT;
+    impl_->presentParams_.FullScreen_RefreshRateInHz = newParams.fullscreen_ ? newParams.refreshRate_ : D3DPRESENT_RATE_DEFAULT;
 
-    if (vsync)
+    if (newParams.vsync_)
         impl_->presentParams_.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
     else
         impl_->presentParams_.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
 
     width_ = width;
     height_ = height;
-    fullscreen_ = fullscreen;
-    borderless_ = borderless;
-    resizable_ = resizable;
-    highDPI_ = highDPI;
-    vsync_ = vsync;
-    tripleBuffer_ = tripleBuffer;
-    monitor_ = monitor;
-    refreshRate_ = refreshRate;
+    screenParams_ = newParams;
 
     if (!impl_->device_)
     {
-        unsigned adapter = SDL_Direct3D9GetAdapterIndex(monitor);
+        unsigned adapter = SDL_Direct3D9GetAdapterIndex(screenParams_.monitor_);
         unsigned deviceType = D3DDEVTYPE_HAL;
 
         // Check for PerfHUD adapter
@@ -496,7 +425,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
             URHO3D_SAFE_RELEASE(impl_->device_);
 
             // create new device on the specified monitor
-            unsigned adapter = SDL_Direct3D9GetAdapterIndex(monitor);
+            unsigned adapter = SDL_Direct3D9GetAdapterIndex(screenParams_.monitor_);
             unsigned deviceType = D3DDEVTYPE_HAL;
             if (!CreateDevice(adapter, deviceType))
                 return false;
@@ -512,45 +441,15 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
 
 #ifdef URHO3D_LOGGING
     D3DADAPTER_IDENTIFIER9 id = {0};
-    HRESULT hr = impl_->interface_->GetAdapterIdentifier(SDL_Direct3D9GetAdapterIndex(monitor_), 0, &id);
+    HRESULT hr = impl_->interface_->GetAdapterIdentifier(SDL_Direct3D9GetAdapterIndex(screenParams_.monitor_), 0, &id);
     if (S_OK == hr)
-      URHO3D_LOGINFOF("Adapter used %s", id.Description);
-
-    String msg;
-    msg.AppendWithFormat("Set screen mode %dx%d rate %d Hz %s monitor %d", width_, height_, refreshRate_,
-        (fullscreen_ ? "fullscreen" : "windowed"), monitor_);
-    if (borderless_)
-        msg.Append(" borderless");
-    if (resizable_)
-        msg.Append(" resizable");
-    if (highDPI_)
-        msg.Append(" highDPI");
-    if (multiSample > 1)
-        msg.AppendWithFormat(" multisample %d", multiSample);
-    URHO3D_LOGINFO(msg);
+        URHO3D_LOGINFOF("Adapter used %s", id.Description);
 #endif
 
-    using namespace ScreenMode;
-
-    VariantMap& eventData = GetEventDataMap();
-    eventData[P_WIDTH] = width_;
-    eventData[P_HEIGHT] = height_;
-    eventData[P_FULLSCREEN] = fullscreen_;
-    eventData[P_BORDERLESS] = borderless_;
-    eventData[P_RESIZABLE] = resizable_;
-    eventData[P_HIGHDPI] = highDPI_;
-    eventData[P_MONITOR] = monitor_;
-    eventData[P_REFRESHRATE] = refreshRate_;
-    SendEvent(E_SCREENMODE, eventData);
-
+    OnScreenModeChanged();
     return true;
 }
 
-bool Graphics::SetMode(int width, int height)
-{
-    return SetMode(width, height, fullscreen_, borderless_, resizable_, highDPI_, vsync_, tripleBuffer_, multiSample_, monitor_, refreshRate_);
-}
-
 void Graphics::SetSRGB(bool enable)
 {
     sRGB_ = enable && sRGBWriteSupport_;
@@ -606,9 +505,9 @@ bool Graphics::TakeScreenShot(Image& destImage)
     if (impl_->presentParams_.MultiSampleType)
     {
         // If windowed and multisampled, must still capture the whole screen
-        if (!fullscreen_)
+        if (!screenParams_.fullscreen_)
         {
-            IntVector2 desktopSize = GetDesktopResolution(monitor_);
+            IntVector2 desktopSize = GetDesktopResolution(screenParams_.monitor_);
             surfaceWidth = (unsigned)desktopSize.x_;
             surfaceHeight = (unsigned)desktopSize.y_;
         }
@@ -728,7 +627,7 @@ bool Graphics::BeginFrame()
     {
         // To prevent a loop of endless device loss and flicker, do not attempt to render when in fullscreen
         // and the window is minimized
-        if (fullscreen_ && (SDL_GetWindowFlags(window_) & SDL_WINDOW_MINIMIZED))
+        if (screenParams_.fullscreen_ && (SDL_GetWindowFlags(window_) & SDL_WINDOW_MINIMIZED))
             return false;
     }
 
@@ -2129,16 +2028,16 @@ void Graphics::OnWindowResized()
     VariantMap& eventData = GetEventDataMap();
     eventData[P_WIDTH] = width_;
     eventData[P_HEIGHT] = height_;
-    eventData[P_FULLSCREEN] = fullscreen_;
-    eventData[P_RESIZABLE] = resizable_;
-    eventData[P_BORDERLESS] = borderless_;
-    eventData[P_HIGHDPI] = highDPI_;
+    eventData[P_FULLSCREEN] = screenParams_.fullscreen_;
+    eventData[P_RESIZABLE] = screenParams_.resizable_;
+    eventData[P_BORDERLESS] = screenParams_.borderless_;
+    eventData[P_HIGHDPI] = screenParams_.highDPI_;
     SendEvent(E_SCREENMODE, eventData);
 }
 
 void Graphics::OnWindowMoved()
 {
-    if (!impl_->device_ || !window_ || fullscreen_)
+    if (!impl_->device_ || !window_ || screenParams_.fullscreen_)
         return;
 
     int newX, newY;

+ 168 - 1
Source/Urho3D/Graphics/Graphics.cpp

@@ -31,6 +31,7 @@
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DecalSet.h"
 #include "../Graphics/Graphics.h"
+#include "../Graphics/GraphicsEvents.h"
 #include "../Graphics/GraphicsImpl.h"
 #include "../Graphics/Material.h"
 #include "../Graphics/Octree.h"
@@ -102,9 +103,41 @@ void Graphics::SetOrientations(const String& orientations)
     SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
 }
 
+bool Graphics::SetScreenMode(int width, int height)
+{
+    return SetScreenMode(width, height, screenParams_);
+}
+
+bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable,
+    bool highDPI, bool vsync, bool tripleBuffer, int multiSample, int monitor, int refreshRate)
+{
+    ScreenModeParams newParams;
+    newParams.fullscreen_ = fullscreen;
+    newParams.borderless_ = borderless;
+    newParams.resizable_ = resizable;
+    newParams.highDPI_ = highDPI;
+    newParams.vsync_ = vsync;
+    newParams.tripleBuffer_ = tripleBuffer;
+    newParams.multiSample_ = multiSample;
+    newParams.monitor_ = monitor;
+    newParams.refreshRate_ = refreshRate;
+
+    const bool maximize = (!width || !height) && !fullscreen && !borderless && resizable;
+    return SetScreenMode(width, height, newParams, maximize);
+}
+
+bool Graphics::SetMode(int width, int height)
+{
+    return SetMode(width, height, screenParams_.fullscreen_, screenParams_.borderless_, screenParams_.resizable_,
+        screenParams_.highDPI_, screenParams_.vsync_, screenParams_.tripleBuffer_,
+        screenParams_.multiSample_, screenParams_.monitor_, screenParams_.refreshRate_);
+}
+
 bool Graphics::ToggleFullscreen()
 {
-    return SetMode(width_, height_, !fullscreen_, borderless_, resizable_, highDPI_, vsync_, tripleBuffer_, multiSample_, monitor_, refreshRate_);
+    ScreenModeParams newParams = screenParams_;
+    newParams.fullscreen_ = !newParams.fullscreen_;
+    return SetScreenMode(width_, height_, newParams);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Variant& value)
@@ -211,6 +244,30 @@ PODVector<IntVector3> Graphics::GetResolutions(int monitor) const
     return ret;
 }
 
+unsigned Graphics::FindBestResolution(int monitor, int width, int height, int refreshRate) const
+{
+    const PODVector<IntVector3> resolutions = GetResolutions(monitor);
+    if (resolutions.Empty())
+        return M_MAX_UNSIGNED;
+
+    unsigned best = 0;
+    unsigned bestError = M_MAX_UNSIGNED;
+
+    for (unsigned i = 0; i < resolutions.Size(); ++i)
+    {
+        auto error = static_cast<unsigned>(Abs(resolutions[i].x_ - width) + Abs(resolutions[i].y_ - height));
+        if (refreshRate != 0)
+            error += static_cast<unsigned>(Abs(resolutions[i].z_ - refreshRate));
+        if (error < bestError)
+        {
+            best = i;
+            bestError = error;
+        }
+    }
+
+    return best;
+}
+
 IntVector2 Graphics::GetDesktopResolution(int monitor) const
 {
 #if !defined(__ANDROID__) && !defined(IOS) && !defined(TVOS)
@@ -398,6 +455,116 @@ void Graphics::CreateWindowIcon()
     }
 }
 
+void Graphics::AdjustScreenMode(int& newWidth, int& newHeight, ScreenModeParams& params, bool& maximize) const
+{
+    // High DPI is supported only for OpenGL backend
+#ifndef URHO3D_OPENGL
+    params.highDPI_ = false;
+#endif;
+
+#if defined(IOS) || defined(TVOS)
+    // iOS and tvOS app always take the fullscreen (and with status bar hidden)
+    params.fullscreen_ = true;
+#endif
+
+    // Make sure monitor index is not bigger than the currently detected monitors
+    const int numMonitors = SDL_GetNumVideoDisplays();
+    if (params.monitor_ >= numMonitors || params.monitor_ < 0)
+        params.monitor_ = 0; // this monitor is not present, use first monitor
+
+    // Fullscreen or Borderless can not be resizable and cannot be maximized
+    if (params.fullscreen_ || params.borderless_)
+    {
+        params.resizable_ = false;
+        maximize = false;
+    }
+
+    // Borderless cannot be fullscreen, they are mutually exclusive
+    if (params.borderless_)
+        params.fullscreen_ = false;
+
+    // On iOS window needs to be resizable to handle orientation changes properly
+#ifdef IOS
+    if (!externalWindow_)
+        params.resizable_ = true;
+#endif
+
+    // Ensure that multisampl factor is in valid range
+    params.multiSample_ = Clamp(params.multiSample_, 1, 16);
+
+    // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size.
+    // If zero in fullscreen, use desktop mode
+    if (!newWidth || !newHeight)
+    {
+        if (params.fullscreen_ || params.borderless_)
+        {
+            SDL_DisplayMode mode;
+            SDL_GetDesktopDisplayMode(params.monitor_, &mode);
+            newWidth = mode.w;
+            newHeight = mode.h;
+        }
+        else
+        {
+            newWidth = 1024;
+            newHeight = 768;
+        }
+    }
+
+    // Check fullscreen mode validity (desktop only). Use a closest match if not found
+#ifdef DESKTOP_GRAPHICS
+    if (params.fullscreen_)
+    {
+        const PODVector<IntVector3> resolutions = GetResolutions(params.monitor_);
+        if (!resolutions.Empty())
+        {
+            const unsigned bestResolution = FindBestResolution(params.monitor_,
+                newWidth, newHeight, params.refreshRate_);
+            newWidth = resolutions[bestResolution].x_;
+            newHeight = resolutions[bestResolution].y_;
+            params.refreshRate_ = resolutions[bestResolution].z_;
+        }
+    }
+    else
+    {
+        // If windowed, use the same refresh rate as desktop
+        SDL_DisplayMode mode;
+        SDL_GetDesktopDisplayMode(params.monitor_, &mode);
+        params.refreshRate_ = mode.refresh_rate;
+    }
+#endif
+}
+
+void Graphics::OnScreenModeChanged()
+{
+#ifdef URHO3D_LOGGING
+    String msg;
+    msg.AppendWithFormat("Set screen mode %dx%d rate %d Hz %s monitor %d", width_, height_, screenParams_.refreshRate_,
+        (screenParams_.fullscreen_ ? "fullscreen" : "windowed"), screenParams_.monitor_);
+    if (screenParams_.borderless_)
+        msg.Append(" borderless");
+    if (screenParams_.resizable_)
+        msg.Append(" resizable");
+    if (screenParams_.highDPI_)
+        msg.Append(" highDPI");
+    if (screenParams_.multiSample_ > 1)
+        msg.AppendWithFormat(" multisample %d", screenParams_.multiSample_);
+    URHO3D_LOGINFO(msg);
+#endif
+
+    using namespace ScreenMode;
+
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_WIDTH] = width_;
+    eventData[P_HEIGHT] = height_;
+    eventData[P_FULLSCREEN] = screenParams_.fullscreen_;
+    eventData[P_BORDERLESS] = screenParams_.borderless_;
+    eventData[P_RESIZABLE] = screenParams_.resizable_;
+    eventData[P_HIGHDPI] = screenParams_.highDPI_;
+    eventData[P_MONITOR] = screenParams_.monitor_;
+    eventData[P_REFRESHRATE] = screenParams_.refreshRate_;
+    SendEvent(E_SCREENMODE, eventData);
+}
+
 void RegisterGraphicsLibrary(Context* context)
 {
     Animation::RegisterObject(context);

+ 79 - 36
Source/Urho3D/Graphics/Graphics.h

@@ -77,6 +77,53 @@ struct ScratchBuffer
     bool reserved_;
 };
 
+/// Screen mode parameters.
+struct ScreenModeParams
+{
+    /// Whether to use fullscreen mode.
+    bool fullscreen_{};
+    /// Whether to hide window borders. Window is always borderless in fullscreen.
+    bool borderless_{};
+    /// Whether the window is resizable.
+    bool resizable_{};
+    /// Whether the high DPI is enabled.
+    /// TODO: Explain what exactly it means.
+    bool highDPI_{};
+    /// Whether the vertical synchronization is used.
+    bool vsync_{};
+    /// Whether the triple bufferization is used.
+    bool tripleBuffer_{};
+    /// Level of multisampling.
+    int multiSample_{ 1 };
+    /// Monitor for fullscreen mode. Has no effect in windowed mode.
+    int monitor_{};
+    /// Refresh rate. 0 to pick automatically.
+    int refreshRate_{};
+
+    /// Compare contents except vsync flag.
+    bool EqualsExceptVSync(const ScreenModeParams& rhs) const
+    {
+        return fullscreen_ == rhs.fullscreen_
+            && borderless_ == rhs.borderless_
+            && resizable_ == rhs.resizable_
+            && highDPI_ == rhs.highDPI_
+            // && vsync_ == rhs.vsync_
+            && tripleBuffer_ == rhs.tripleBuffer_
+            && multiSample_ == rhs.multiSample_
+            && monitor_ == rhs.monitor_
+            && refreshRate_ == rhs.refreshRate_;
+    }
+
+    /// Compare for equality with another parameter set.
+    bool operator ==(const ScreenModeParams& rhs) const
+    {
+        return vsync_ == rhs.vsync_ && EqualsExceptVSync(rhs);
+    }
+
+    /// Compare for inequality with another parameter set.
+    bool operator !=(const ScreenModeParams& rhs) const { return !(*this == rhs); }
+};
+
 /// %Graphics subsystem. Manages the application window, rendering state and GPU resources.
 class URHO3D_API Graphics : public Object
 {
@@ -99,10 +146,13 @@ public:
     /// Set window position. Sets initial position if window is not created yet.
     void SetWindowPosition(int x, int y);
     /// Set screen mode. Return true if successful.
-    bool SetMode
-        (int width, int height, bool fullscreen, bool borderless, bool resizable, bool highDPI, bool vsync, bool tripleBuffer,
-            int multiSample, int monitor, int refreshRate);
+    bool SetScreenMode(int width, int height, const ScreenModeParams& params, bool maximize = false);
     /// Set screen resolution only. Return true if successful.
+    bool SetScreenMode(int width, int height);
+    /// Set screen mode. Deprecated. Return true if successful.
+    bool SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable,
+        bool highDPI, bool vsync, bool tripleBuffer, int multiSample, int monitor, int refreshRate);
+    /// Set screen resolution only. Deprecated. Return true if successful.
     bool SetMode(int width, int height);
     /// Set whether the main window uses sRGB conversion on write.
     void SetSRGB(bool enable);
@@ -194,7 +244,7 @@ public:
     void SetTexture(unsigned index, Texture* texture);
     /// Bind texture unit 0 for update. Called by Texture. Used only on OpenGL.
     void SetTextureForUpdate(Texture* texture);
-    /// Dirty texture parameters of all textures (when global settings change).
+    /// Dirty texture parameters of all textures (when global settings change.)
     void SetTextureParametersDirty();
     /// Set default texture filtering mode. Called by Renderer before rendering.
     void SetDefaultTextureFilterMode(TextureFilterMode mode);
@@ -279,35 +329,38 @@ public:
     /// Return window height in pixels.
     int GetHeight() const { return height_; }
 
-    /// Return multisample mode (1 = no multisampling).
-    int GetMultiSample() const { return multiSample_; }
+    /// Return screen mode parameters.
+    const ScreenModeParams& GetScreenModeParams() const { return screenParams_; }
+
+    /// Return multisample mode (1 = no multisampling.)
+    int GetMultiSample() const { return screenParams_.multiSample_; }
 
     /// Return window size in pixels.
     IntVector2 GetSize() const { return IntVector2(width_, height_); }
 
     /// Return whether window is fullscreen.
-    bool GetFullscreen() const { return fullscreen_; }
+    bool GetFullscreen() const { return screenParams_.fullscreen_; }
 
     /// Return whether window is borderless.
-    bool GetBorderless() const { return borderless_; }
+    bool GetBorderless() const { return screenParams_.borderless_; }
 
     /// Return whether window is resizable.
-    bool GetResizable() const { return resizable_; }
+    bool GetResizable() const { return screenParams_.resizable_; }
 
     /// Return whether window is high DPI.
-    bool GetHighDPI() const { return highDPI_; }
+    bool GetHighDPI() const { return screenParams_.highDPI_; }
 
     /// Return whether vertical sync is on.
-    bool GetVSync() const { return vsync_; }
+    bool GetVSync() const { return screenParams_.vsync_; }
 
-    /// Return refresh rate when using vsync in fullscreen.
-    int GetRefreshRate() const { return refreshRate_; }
+    /// Return refresh rate when using vsync in fullscreen
+    int GetRefreshRate() const { return screenParams_.refreshRate_; }
 
-    /// Return the current monitor index. Effective on in fullscreen.
-    int GetMonitor() const { return monitor_; }
+    /// Return the current monitor index. Effective on in fullscreen
+    int GetMonitor() const { return screenParams_.monitor_; }
 
     /// Return whether triple buffering is enabled.
-    bool GetTripleBuffer() const { return tripleBuffer_; }
+    bool GetTripleBuffer() const { return screenParams_.tripleBuffer_; }
 
     /// Return whether the main window is using sRGB conversion on write.
     bool GetSRGB() const { return sRGB_; }
@@ -368,6 +421,8 @@ public:
 
     /// Return supported fullscreen resolutions (third component is refreshRate). Will be empty if listing the resolutions is not supported on the platform (e.g. Web).
     PODVector<IntVector3> GetResolutions(int monitor) const;
+    /// Return index of the best resolution for requested width, height and refresh rate.
+    unsigned FindBestResolution(int monitor, int width, int height, int refreshRate) const;
     /// Return supported multisampling levels.
     PODVector<int> GetMultiSampleLevels() const;
     /// Return the desktop resolution.
@@ -576,10 +631,14 @@ private:
     bool OpenWindow(int width, int height, bool resizable, bool borderless);
     /// Create the application window icon.
     void CreateWindowIcon();
+    /// Adjust parameters according to the platform. Fill in missing paramters and resolve possible conflicts.
+    void AdjustScreenMode(int& newWidth, int& newHeight, ScreenModeParams& params, bool& maximize) const;
+    /// Called when screen mode is successfully changed by the backend.
+    void OnScreenModeChanged();
     /// Adjust the window for new resolution and fullscreen mode.
     void AdjustWindow(int& newWidth, int& newHeight, bool& newFullscreen, bool& newBorderless, int& monitor);
     /// Create the Direct3D11 device and swap chain. Requires an open window. Can also be called again to recreate swap chain. Return true on success.
-    bool CreateDevice(int width, int height, int multiSample);
+    bool CreateDevice(int width, int height);
     /// Update Direct3D11 swap chain state for a new mode and create views for the backbuffer & default depth buffer. Return true on success.
     bool UpdateSwapChain(int width, int height);
     /// Create the Direct3D9 interface.
@@ -637,7 +696,7 @@ private:
     String windowTitle_;
     /// Window icon image.
     WeakPtr<Image> windowIcon_;
-    /// External window, null if not in use (default).
+    /// External window, null if not in use (default.)
     void* externalWindow_{};
     /// Window width in pixels.
     int width_{};
@@ -645,24 +704,8 @@ private:
     int height_{};
     /// Window position.
     IntVector2 position_;
-    /// Multisampling mode.
-    int multiSample_{1};
-    /// Fullscreen flag.
-    bool fullscreen_{};
-    /// Borderless flag.
-    bool borderless_{};
-    /// Resizable flag.
-    bool resizable_{};
-    /// High DPI flag.
-    bool highDPI_{};
-    /// Vertical sync flag.
-    bool vsync_{};
-    /// Refresh rate in Hz. Only used in fullscreen, 0 when windowed.
-    int refreshRate_{};
-    /// Monitor index. Only used in fullscreen, 0 when windowed.
-    int monitor_{};
-    /// Triple buffering flag.
-    bool tripleBuffer_{};
+    /// Screen mode parameters.
+    ScreenModeParams screenParams_;
     /// Flush GPU command buffer flag.
     bool flushGPU_{};
     /// Force OpenGL 2 flag. Only used on OpenGL.

+ 34 - 145
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp

@@ -254,107 +254,32 @@ Graphics::~Graphics()
     context_->ReleaseSDL();
 }
 
-bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable, bool highDPI, bool vsync,
-    bool tripleBuffer, int multiSample, int monitor, int refreshRate)
+bool Graphics::SetScreenMode(int width, int height, const ScreenModeParams& params, bool maximize)
 {
     URHO3D_PROFILE(SetScreenMode);
 
-    bool maximize = false;
+    // Ensure that parameters are properly filled
+    ScreenModeParams newParams = params;
+    AdjustScreenMode(width, height, newParams, maximize);
 
-#if defined(IOS) || defined(TVOS)
-    // iOS and tvOS app always take the fullscreen (and with status bar hidden)
-    fullscreen = true;
-#endif
-
-    // Make sure monitor index is not bigger than the currently detected monitors
-    int monitors = SDL_GetNumVideoDisplays();
-    if (monitor >= monitors || monitor < 0)
-        monitor = 0; // this monitor is not present, use first monitor
-
-    // Fullscreen or Borderless can not be resizable
-    if (fullscreen || borderless)
-        resizable = false;
-
-    // Borderless cannot be fullscreen, they are mutually exclusive
-    if (borderless)
-        fullscreen = false;
-
-    multiSample = Clamp(multiSample, 1, 16);
-
-    if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ &&
-        resizable == resizable_ && vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_ &&
-        monitor == monitor_ && refreshRate == refreshRate_)
+    if (IsInitialized() && width == width_ && height == height_ && screenParams_ == newParams)
         return true;
 
     // If only vsync changes, do not destroy/recreate the context
-    if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ &&
-        resizable == resizable_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_ && monitor == monitor_ &&
-        refreshRate == refreshRate_ && vsync != vsync_)
+    if (IsInitialized() && width == width_ && height == height_
+        && screenParams_.EqualsExceptVSync(newParams) && screenParams_.vsync_ != newParams.vsync_)
     {
-        SDL_GL_SetSwapInterval(vsync ? 1 : 0);
-        vsync_ = vsync;
+        SDL_GL_SetSwapInterval(newParams.vsync_ ? 1 : 0);
+        screenParams_.vsync_ = newParams.vsync_;
         return true;
     }
 
-    // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size.
-    // If zero in fullscreen, use desktop mode
-    if (!width || !height)
-    {
-        if (fullscreen || borderless)
-        {
-            SDL_DisplayMode mode;
-            SDL_GetDesktopDisplayMode(monitor, &mode);
-            width = mode.w;
-            height = mode.h;
-        }
-        else
-        {
-            maximize = resizable;
-            width = 1024;
-            height = 768;
-        }
-    }
-
-    // Check fullscreen mode validity (desktop only). Use a closest match if not found
-#ifdef DESKTOP_GRAPHICS
-    if (fullscreen)
-    {
-        PODVector<IntVector3> resolutions = GetResolutions(monitor);
-        if (resolutions.Size())
-        {
-            unsigned best = 0;
-            unsigned bestError = M_MAX_UNSIGNED;
-
-            for (unsigned i = 0; i < resolutions.Size(); ++i)
-            {
-                unsigned error = (unsigned)(Abs(resolutions[i].x_ - width) + Abs(resolutions[i].y_ - height));
-                if (refreshRate != 0)
-                    error += (unsigned)(Abs(resolutions[i].z_ - refreshRate));
-                if (error < bestError)
-                {
-                    best = i;
-                    bestError = error;
-                }
-            }
-
-            width = resolutions[best].x_;
-            height = resolutions[best].y_;
-            refreshRate = resolutions[best].z_;
-        }
-    }
-#endif
-
     // With an external window, only the size can change after initial setup, so do not recreate context
     if (!externalWindow_ || !impl_->context_)
     {
         // Close the existing window and OpenGL context, mark GPU objects as lost
         Release(false, true);
 
-#ifdef IOS
-        // On iOS window needs to be resizable to handle orientation changes properly
-        resizable = true;
-#endif
-
         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 
 #ifndef GL_ES_VERSION_2_0
@@ -387,10 +312,10 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
 #endif
 
-        if (multiSample > 1)
+        if (newParams.multiSample_ > 1)
         {
             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
-            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multiSample);
+            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, newParams.multiSample_);
         }
         else
         {
@@ -399,20 +324,20 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         }
 
         SDL_Rect display_rect;
-        SDL_GetDisplayBounds(monitor, &display_rect);
-        bool reposition = fullscreen || (borderless && width >= display_rect.w && height >= display_rect.h);
+        SDL_GetDisplayBounds(newParams.monitor_, &display_rect);
+        const bool reposition = newParams.fullscreen_ || (newParams.borderless_ && width >= display_rect.w && height >= display_rect.h);
 
-        int x = reposition ? display_rect.x : position_.x_;
-        int y = reposition ? display_rect.y : position_.y_;
+        const int x = reposition ? display_rect.x : position_.x_;
+        const int y = reposition ? display_rect.y : position_.y_;
 
         unsigned flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
-        if (fullscreen)
+        if (newParams.fullscreen_)
             flags |= SDL_WINDOW_FULLSCREEN;
-        if (borderless)
+        if (newParams.borderless_)
             flags |= SDL_WINDOW_BORDERLESS;
-        if (resizable)
+        if (newParams.resizable_)
             flags |= SDL_WINDOW_RESIZABLE;
-        if (highDPI)
+        if (newParams.highDPI_)
             flags |= SDL_WINDOW_ALLOW_HIGHDPI;
 
         SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
@@ -426,7 +351,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
 #ifndef __EMSCRIPTEN__
                 if (!window_)
                     window_ = SDL_CreateWindowFrom(externalWindow_, SDL_WINDOW_OPENGL);
-                fullscreen = false;
+                newParams.fullscreen_ = false;
 #endif
             }
 
@@ -434,10 +359,10 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
                 break;
             else
             {
-                if (multiSample > 1)
+                if (newParams.multiSample_ > 1)
                 {
                     // If failed with multisampling, retry first without
-                    multiSample = 1;
+                    newParams.multiSample_ = 1;
                     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
                     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
                 }
@@ -470,29 +395,22 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
     }
 
     // Set vsync
-    SDL_GL_SetSwapInterval(vsync ? 1 : 0);
+    SDL_GL_SetSwapInterval(newParams.vsync_ ? 1 : 0);
 
     // Store the system FBO on iOS/tvOS now
 #if defined(IOS) || defined(TVOS)
     glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&impl_->systemFBO_);
 #endif
 
-    fullscreen_ = fullscreen;
-    borderless_ = borderless;
-    resizable_ = resizable;
-    vsync_ = vsync;
-    tripleBuffer_ = tripleBuffer;
-    multiSample_ = multiSample;
-    monitor_ = monitor;
-    refreshRate_ = refreshRate;
+    screenParams_ = newParams;
 
     SDL_GL_GetDrawableSize(window_, &width_, &height_);
-    if (!fullscreen)
+    if (!screenParams_.fullscreen_)
         SDL_GetWindowPosition(window_, &position_.x_, &position_.y_);
 
     int logicalWidth, logicalHeight;
     SDL_GetWindowSize(window_, &logicalWidth, &logicalHeight);
-    highDPI_ = (width_ != logicalWidth) || (height_ != logicalHeight);
+    screenParams_.highDPI_ = (width_ != logicalWidth) || (height_ != logicalHeight);
 
     // Reset rendertargets and viewport for the new screen mode
     ResetRenderTargets();
@@ -505,41 +423,12 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
 
 #ifdef URHO3D_LOGGING
     URHO3D_LOGINFOF("Adapter used %s %s", (const char *) glGetString(GL_VENDOR), (const char *) glGetString(GL_RENDERER));
-
-    String msg;
-    msg.AppendWithFormat("Set screen mode %dx%d rate %d Hz %s monitor %d", width_, height_, refreshRate_, (fullscreen_ ? "fullscreen" : "windowed"), monitor_);
-    if (borderless_)
-        msg.Append(" borderless");
-    if (resizable_)
-        msg.Append(" resizable");
-    if (highDPI_)
-        msg.Append(" highDPI");
-    if (multiSample > 1)
-        msg.AppendWithFormat(" multisample %d", multiSample);
-    URHO3D_LOGINFO(msg);
 #endif
 
-    using namespace ScreenMode;
-
-    VariantMap& eventData = GetEventDataMap();
-    eventData[P_WIDTH] = width_;
-    eventData[P_HEIGHT] = height_;
-    eventData[P_FULLSCREEN] = fullscreen_;
-    eventData[P_BORDERLESS] = borderless_;
-    eventData[P_RESIZABLE] = resizable_;
-    eventData[P_HIGHDPI] = highDPI_;
-    eventData[P_MONITOR] = monitor_;
-    eventData[P_REFRESHRATE] = refreshRate_;
-    SendEvent(E_SCREENMODE, eventData);
-
+    OnScreenModeChanged();
     return true;
 }
 
-bool Graphics::SetMode(int width, int height)
-{
-    return SetMode(width, height, fullscreen_, borderless_, resizable_, highDPI_, vsync_, tripleBuffer_, multiSample_, monitor_, refreshRate_);
-}
-
 void Graphics::SetSRGB(bool enable)
 {
     enable &= sRGBWriteSupport_;
@@ -2252,7 +2141,7 @@ void Graphics::OnWindowResized()
 
     int logicalWidth, logicalHeight;
     SDL_GetWindowSize(window_, &logicalWidth, &logicalHeight);
-    highDPI_ = (width_ != logicalWidth) || (height_ != logicalHeight);
+    screenParams_.highDPI_ = (width_ != logicalWidth) || (height_ != logicalHeight);
 
     // Reset rendertargets and viewport for the new screen size. Also clean up any FBO's, as they may be screen size dependent
     CleanupFramebuffers();
@@ -2265,16 +2154,16 @@ void Graphics::OnWindowResized()
     VariantMap& eventData = GetEventDataMap();
     eventData[P_WIDTH] = width_;
     eventData[P_HEIGHT] = height_;
-    eventData[P_FULLSCREEN] = fullscreen_;
-    eventData[P_RESIZABLE] = resizable_;
-    eventData[P_BORDERLESS] = borderless_;
-    eventData[P_HIGHDPI] = highDPI_;
+    eventData[P_FULLSCREEN] = screenParams_.fullscreen_;
+    eventData[P_RESIZABLE] = screenParams_.resizable_;
+    eventData[P_BORDERLESS] = screenParams_.borderless_;
+    eventData[P_HIGHDPI] = screenParams_.highDPI_;
     SendEvent(E_SCREENMODE, eventData);
 }
 
 void Graphics::OnWindowMoved()
 {
-    if (!window_ || fullscreen_)
+    if (!window_ || screenParams_.fullscreen_)
         return;
 
     int newX, newY;
@@ -2409,7 +2298,7 @@ void Graphics::Release(bool clearGPUObjects, bool closeWindow)
 
     // End fullscreen mode first to counteract transition and getting stuck problems on OS X
 #if defined(__APPLE__) && !defined(IOS) && !defined(TVOS)
-    if (closeWindow && fullscreen_ && !externalWindow_)
+    if (closeWindow && screenParams_.fullscreen_ && !externalWindow_)
         SDL_SetWindowFullscreen(window_, 0);
 #endif