1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-08 22:26:51 +02:00

Merge pull request #5411 from IvanSavenko/hw_accel_prep

[1.7] SDL access cleanup and encapsulation
This commit is contained in:
Ivan Savenko
2025-02-20 16:32:39 +02:00
committed by GitHub
19 changed files with 108 additions and 213 deletions

View File

@ -9,17 +9,9 @@
*/
#pragma once
struct SDL_Texture;
struct SDL_Renderer;
struct SDL_Surface;
extern SDL_Texture * screenTexture;
extern SDL_Renderer * mainRenderer;
extern SDL_Surface *screen; // main screen surface
extern SDL_Surface *screen2; // and hlp surface (used to store not-active interfaces layer)
extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
/// Notify user about encountered fatal error and terminate the game
/// Defined in clientapp EntryPoint
/// TODO: decide on better location for this method

View File

@ -34,7 +34,6 @@
#include "../render/IImage.h"
#include "../render/IRenderHandler.h"
#include "../render/IScreenHandler.h"
#include "../CMT.h"
#include "../PlayerLocalState.h"
#include "../CPlayerInterface.h"
@ -131,8 +130,6 @@ void AdventureMapInterface::activate()
adjustActiveness();
screenBuf = screen;
if(LOCPLINT)
{
LOCPLINT->cingconsole->activate();
@ -453,8 +450,7 @@ void AdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
widget->getInfoBar()->showDate();
onHeroChanged(nullptr);
Canvas canvas = Canvas::createFromSurface(screen, CanvasScalingPolicy::AUTO);
showAll(canvas);
GH.windows().totalRedraw();
mapAudio->onPlayerTurnStarted();
if(settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown())

View File

@ -20,13 +20,13 @@
#include "../CGameInfo.h"
#include "../adventureMap/AdventureMapInterface.h"
#include "../render/Canvas.h"
#include "../render/Colors.h"
#include "../render/Graphics.h"
#include "../render/IFont.h"
#include "../render/EFont.h"
#include "../renderSDL/ScreenHandler.h"
#include "../renderSDL/RenderHandler.h"
#include "../CMT.h"
#include "../CPlayerInterface.h"
#include "../battle/BattleInterface.h"
@ -104,21 +104,13 @@ void CGuiHandler::renderFrame()
if (settings["video"]["showfps"].Bool())
drawFPSCounter();
SDL_UpdateTexture(screenTexture, nullptr, screen->pixels, screen->pitch);
}
SDL_RenderClear(mainRenderer);
SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr);
{
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
CCS->curh->render();
screenHandlerInstance->updateScreenTexture();
windows().onFrameRendered();
CCS->curh->update();
}
SDL_RenderPresent(mainRenderer);
screenHandlerInstance->presentScreenTexture();
framerate().framerateDelay(); // holds a constant FPS
}
@ -181,19 +173,12 @@ Point CGuiHandler::screenDimensions() const
void CGuiHandler::drawFPSCounter()
{
int scaling = screenHandlerInstance->getScalingFactor();
int x = 7 * scaling;
int y = screen->h-20 * scaling;
int width3digitFPSIncludingPadding = 48 * scaling;
int heightFPSTextIncludingPadding = 11 * scaling;
SDL_Rect overlay = { x, y, width3digitFPSIncludingPadding, heightFPSTextIncludingPadding};
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
SDL_FillRect(screen, &overlay, black);
Canvas target = GH.screenHandler().getScreenCanvas();
Rect targetArea(0, screenDimensions().y - 20, 48, 11);
std::string fps = std::to_string(framerate().getFramerate())+" FPS";
const auto & font = GH.renderHandler().loadFont(FONT_SMALL);
font->renderTextLeft(screen, fps, Colors::WHITE, Point(8 * scaling, screen->h-22 * scaling));
target.drawColor(targetArea, ColorRGBA(10, 10, 10));
target.drawText(targetArea.center(), EFonts::FONT_SMALL, Colors::WHITE, ETextAlignment::CENTER, fps);
}
bool CGuiHandler::amIGuiThread()

View File

@ -15,8 +15,8 @@
#include "EventDispatcher.h"
#include "Shortcut.h"
#include "../render/Canvas.h"
#include "../render/IScreenHandler.h"
#include "../windows/CMessage.h"
#include "../CMT.h"
CIntObject::CIntObject(int used_, Point pos_):
parent_m(nullptr),
@ -238,15 +238,8 @@ void CIntObject::redraw()
}
else
{
Canvas buffer = Canvas::createFromSurface(screenBuf, CanvasScalingPolicy::AUTO);
Canvas buffer = GH.screenHandler().getScreenCanvas();
showAll(buffer);
if(screenBuf != screen)
{
Canvas screenBuffer = Canvas::createFromSurface(screen, CanvasScalingPolicy::AUTO);
showAll(screenBuffer);
}
}
}
}

View File

@ -260,6 +260,11 @@ void CursorHandler::updateSpellcastCursor()
}
void CursorHandler::render()
{
cursor->render();
}
void CursorHandler::update()
{
if(!showing)
return;
@ -267,7 +272,7 @@ void CursorHandler::render()
if (type == Cursor::Type::SPELLBOOK)
updateSpellcastCursor();
cursor->render();
cursor->update();
}
void CursorHandler::hide()

View File

@ -179,6 +179,7 @@ public:
Point getPivotOffsetCombat(size_t index);
void render();
void update();
void hide();
void show();

View File

@ -14,11 +14,10 @@
#include "CIntObject.h"
#include "CursorHandler.h"
#include "../CMT.h"
#include "../CGameInfo.h"
#include "../render/Canvas.h"
#include "../render/IScreenHandler.h"
#include "../render/Colors.h"
#include "../renderSDL/SDL_Extensions.h"
void WindowHandler::popWindow(std::shared_ptr<IShowActivatable> top)
{
@ -42,9 +41,6 @@ void WindowHandler::pushWindow(std::shared_ptr<IShowActivatable> newInt)
if (vstd::contains(windowsStack, newInt))
throw std::runtime_error("Attempt to add already existing window to stack!");
//a new interface will be present, we'll need to use buffer surface (unless it's advmapint that will alter screenBuf on activate anyway)
screenBuf = screen2;
if(!windowsStack.empty())
windowsStack.back()->deactivate();
windowsStack.push_back(newInt);
@ -111,11 +107,10 @@ void WindowHandler::totalRedrawImpl()
{
logGlobal->debug("totalRedraw requested!");
Canvas target = Canvas::createFromSurface(screen2, CanvasScalingPolicy::AUTO);
Canvas target = GH.screenHandler().getScreenCanvas();
for(auto & elem : windowsStack)
elem->showAll(target);
CSDL_Ext::blitAt(screen2, 0, 0, screen);
}
void WindowHandler::simpleRedraw()
@ -130,11 +125,7 @@ void WindowHandler::simpleRedraw()
void WindowHandler::simpleRedrawImpl()
{
//update only top interface and draw background
if(windowsStack.size() > 1)
CSDL_Ext::blitAt(screen2, 0, 0, screen); //blit background
Canvas target = Canvas::createFromSurface(screen, CanvasScalingPolicy::AUTO);
Canvas target = GH.screenHandler().getScreenCanvas();
if(!windowsStack.empty())
windowsStack.back()->show(target); //blit active interface/window

View File

@ -23,6 +23,7 @@ public:
virtual void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) = 0;
virtual void setCursorPosition( const Point & newPos ) = 0;
virtual void render() = 0;
virtual void update() = 0;
virtual void setVisible( bool on) = 0;
};

View File

@ -15,6 +15,8 @@ class Point;
class Rect;
VCMI_LIB_NAMESPACE_END
class Canvas;
class IScreenHandler
{
public:
@ -29,6 +31,15 @@ public:
/// Fills screen with black color, erasing any existing content
virtual void clearScreen() = 0;
/// Returns canvas that can be used to display objects on screen
virtual Canvas getScreenCanvas() const = 0;
/// Synchronizes internal screen texture. Screen canvas may not be modified during this call
virtual void updateScreenTexture() = 0;
/// Presents screen texture on the screen
virtual void presentScreenTexture() = 0;
/// Returns list of resolutions supported by current screen
virtual std::vector<Point> getSupportedResolutions() const = 0;

View File

@ -92,3 +92,8 @@ void CursorHardware::render()
{
//no-op
}
void CursorHardware::update()
{
//no-op
}

View File

@ -30,6 +30,7 @@ public:
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
void setCursorPosition( const Point & newPos ) override;
void render() override;
void update() override;
void setVisible( bool on) override;
};

View File

@ -21,12 +21,15 @@
#include <SDL_render.h>
#include <SDL_events.h>
void CursorSoftware::render()
void CursorSoftware::update()
{
//texture must be updated in the main (renderer) thread, but changes to cursor type may come from other threads
if (needUpdate)
updateTexture();
}
void CursorSoftware::render()
{
Point renderPos = pos - pivot;
SDL_Rect destRect;

View File

@ -38,6 +38,7 @@ public:
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
void setCursorPosition( const Point & newPos ) override;
void render() override;
void update() override;
void setVisible( bool on) override;
};

View File

@ -324,10 +324,21 @@ void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palet
bool SDLImageShared::isTransparent(const Point & coords) const
{
assert(upscalingInProgress == false);
if (surf)
return CSDL_Ext::isTransparent(surf, coords.x - margins.x, coords.y - margins.y);
else
if (!surf)
return true;
Point test = coords - margins;
if (test.x < 0 || test.y < 0 || test.x >= surf->w || test.y >= surf->h)
return true;
SDL_Color color;
SDL_GetRGBA(CSDL_Ext::getPixel(surf, test.x, test.y), surf->format, &color.r, &color.g, &color.b, &color.a);
bool pixelTransparent = color.a < 128;
bool pixelCyan = (color.r == 0 && color.g == 255 && color.b == 255);
return pixelTransparent || pixelCyan;
}
Rect SDLImageShared::contentRect() const

View File

@ -12,7 +12,6 @@
#include "SDL_Extensions.h"
#include "../CMT.h"
#include "../xBRZ/xbrz.h"
#include <tbb/parallel_for.h>
@ -211,7 +210,7 @@ SDLImageScaler::SDLImageScaler(SDL_Surface * surf, const Rect & virtualDimension
if (optimizeImage)
{
SDLImageOptimizer optimizer(surf, virtualDimensions);
optimizer.optimizeSurface(screen);
optimizer.optimizeSurface(nullptr);
intermediate = optimizer.acquireResultSurface();
virtualDimensionsInput = optimizer.getResultDimensions();
}

View File

@ -59,36 +59,31 @@ SDL_Color CSDL_Ext::toSDL(const ColorRGBA & color)
return result;
}
void CSDL_Ext::setColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors)
{
SDL_SetPaletteColors(surface->format->palette,colors,firstcolor,ncolors);
}
void CSDL_Ext::setAlpha(SDL_Surface * bg, int value)
{
SDL_SetSurfaceAlphaMod(bg, value);
}
SDL_Surface * CSDL_Ext::newSurface(const Point & dimensions)
{
return newSurface(dimensions, screen);
return newSurface(dimensions, nullptr);
}
SDL_Surface * CSDL_Ext::newSurface(const Point & dimensions, SDL_Surface * mod) //creates new surface, with flags/format same as in surface given
{
SDL_Surface * ret = SDL_CreateRGBSurface(0,dimensions.x,dimensions.y,mod->format->BitsPerPixel,mod->format->Rmask,mod->format->Gmask,mod->format->Bmask,mod->format->Amask);
SDL_Surface * ret = nullptr;
if (mod != nullptr)
ret = SDL_CreateRGBSurface(0,dimensions.x,dimensions.y,mod->format->BitsPerPixel,mod->format->Rmask,mod->format->Gmask,mod->format->Bmask,mod->format->Amask);
else
ret = SDL_CreateRGBSurfaceWithFormat(0,dimensions.x,dimensions.y,32,SDL_PixelFormatEnum::SDL_PIXELFORMAT_ARGB8888);
if(ret == nullptr)
{
const char * error = SDL_GetError();
std::string messagePattern = "Failed to create SDL Surface of size %d x %d, %d bpp. Reason: %s";
std::string message = boost::str(boost::format(messagePattern) % dimensions.x % dimensions.y % mod->format->BitsPerPixel % error);
std::string messagePattern = "Failed to create SDL Surface of size %d x %d. Reason: %s";
std::string message = boost::str(boost::format(messagePattern) % dimensions.x % dimensions.y % error);
handleFatalError(message, true);
}
if (mod->format->palette)
if (mod && mod->format->palette)
{
assert(ret->format->palette);
assert(ret->format->palette->ncolors >= mod->format->palette->ncolors);
@ -97,25 +92,6 @@ SDL_Surface * CSDL_Ext::newSurface(const Point & dimensions, SDL_Surface * mod)
return ret;
}
SDL_Surface * CSDL_Ext::copySurface(SDL_Surface * mod) //returns copy of given surface
{
//return SDL_DisplayFormat(mod);
return SDL_ConvertSurface(mod, mod->format, mod->flags);
}
template<int bpp>
SDL_Surface * CSDL_Ext::createSurfaceWithBpp(int width, int height)
{
uint32_t rMask = 0, gMask = 0, bMask = 0, aMask = 0;
Channels::px<bpp>::r.set((uint8_t*)&rMask, 255);
Channels::px<bpp>::g.set((uint8_t*)&gMask, 255);
Channels::px<bpp>::b.set((uint8_t*)&bMask, 255);
Channels::px<bpp>::a.set((uint8_t*)&aMask, 255);
return SDL_CreateRGBSurface(0, width, height, bpp * 8, rMask, gMask, bMask, aMask);
}
void CSDL_Ext::blitAt(SDL_Surface * src, int x, int y, SDL_Surface * dst)
{
CSDL_Ext::blitSurface(src, dst, Point(x, y));
@ -537,54 +513,23 @@ void CSDL_Ext::drawBorder( SDL_Surface * sur, const Rect &r, const SDL_Color &co
drawBorder(sur, r.x, r.y, r.w, r.h, color, depth);
}
CSDL_Ext::TColorPutter CSDL_Ext::getPutterFor(SDL_Surface * const &dest)
{
switch(dest->format->BytesPerPixel)
{
case 3:
return ColorPutter<3>::PutColor;
case 4:
return ColorPutter<4>::PutColor;
default:
logGlobal->error("%d bpp is not supported!", (int)dest->format->BitsPerPixel);
return nullptr;
}
}
uint8_t * CSDL_Ext::getPxPtr(const SDL_Surface * const &srf, const int x, const int y)
{
return (uint8_t *)srf->pixels + y * srf->pitch + x * srf->format->BytesPerPixel;
}
bool CSDL_Ext::isTransparent( SDL_Surface * srf, const Point & position )
{
return isTransparent(srf, position.x, position.y);
}
bool CSDL_Ext::isTransparent( SDL_Surface * srf, int x, int y )
{
if (x < 0 || y < 0 || x >= srf->w || y >= srf->h)
return true;
SDL_Color color;
SDL_GetRGBA(CSDL_Ext::getPixel(srf, x, y), srf->format, &color.r, &color.g, &color.b, &color.a);
bool pixelTransparent = color.a < 128;
bool pixelCyan = (color.r == 0 && color.g == 255 && color.b == 255);
return pixelTransparent || pixelCyan;
}
void CSDL_Ext::putPixelWithoutRefresh(SDL_Surface *ekran, const int & x, const int & y, const uint8_t & R, const uint8_t & G, const uint8_t & B, uint8_t A)
{
uint8_t *p = getPxPtr(ekran, x, y);
getPutterFor(ekran)(p, R, G, B);
switch(ekran->format->BytesPerPixel)
{
case 3: Channels::px<3>::a.set(p, A); break;
case 4: Channels::px<4>::a.set(p, A); break;
case 3:
ColorPutter<3>::PutColor(p, R, G, B);
Channels::px<3>::a.set(p, A); break;
case 4:
ColorPutter<4>::PutColor(p, R, G, B);
Channels::px<4>::a.set(p, A); break;
}
}
@ -719,6 +664,3 @@ void CSDL_Ext::getClipRect(SDL_Surface * src, Rect & other)
other = CSDL_Ext::fromSDL(rect);
}
template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int);
template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<4>(int, int);

View File

@ -13,20 +13,9 @@
#include "../../lib/Color.h"
struct SDL_Rect;
struct SDL_Window;
struct SDL_Renderer;
struct SDL_Texture;
struct SDL_Surface;
struct SDL_Color;
VCMI_LIB_NAMESPACE_BEGIN
class PlayerColor;
class Rect;
class Point;
VCMI_LIB_NAMESPACE_END
namespace CSDL_Ext
{
@ -42,12 +31,6 @@ ColorRGBA fromSDL(const SDL_Color & color);
/// creates SDL_Color using provided Color
SDL_Color toSDL(const ColorRGBA & color);
void setColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors);
void setAlpha(SDL_Surface * bg, int value);
using TColorPutter = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, const uint8_t &);
using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, const uint8_t &, const uint8_t &);
void blitAt(SDL_Surface * src, int x, int y, SDL_Surface * dst);
void blitAt(SDL_Surface * src, const Rect & pos, SDL_Surface * dst);
@ -67,11 +50,8 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
SDL_Surface * verticalFlip(SDL_Surface * toRot); //vertical flip
SDL_Surface * horizontalFlip(SDL_Surface * toRot); //horizontal flip
uint32_t getPixel(SDL_Surface * surface, const int & x, const int & y, bool colorByte = false);
bool isTransparent(SDL_Surface * srf, int x, int y); //checks if surface is transparent at given position
bool isTransparent(SDL_Surface * srf, const Point & position); //checks if surface is transparent at given position
uint8_t * getPxPtr(const SDL_Surface * const & srf, const int x, const int y);
TColorPutter getPutterFor(SDL_Surface * const & dest);
template<int bpp, bool useAlpha>
int blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint, uint8_t alpha); //blits 8 bpp surface with alpha channel to 24 bpp surface
@ -86,9 +66,6 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
SDL_Surface * newSurface(const Point & dimensions, SDL_Surface * mod); //creates new surface, with flags/format same as in surface given
SDL_Surface * newSurface(const Point & dimensions); //creates new surface, with flags/format same as in screen surface
SDL_Surface * copySurface(SDL_Surface * mod); //returns copy of given surface
template<int bpp>
SDL_Surface * createSurfaceWithBpp(int width, int height); //create surface with give bits per pixels value
template<int bpp>
void convertToGrayscaleBpp(SDL_Surface * surf, const Rect & rect);
@ -100,25 +77,4 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
void setDefaultColorKey(SDL_Surface * surface);
///set key-color to 0,255,255 only if it exactly mapped
void setDefaultColorKeyPresize(SDL_Surface * surface);
/// helper that will safely set and un-set ClipRect for SDL_Surface
class CClipRectGuard: boost::noncopyable
{
SDL_Surface * surf;
Rect oldRect;
int getScalingFactor() const;
public:
CClipRectGuard(SDL_Surface * surface, const Rect & rect): surf(surface)
{
CSDL_Ext::getClipRect(surf, oldRect);
CSDL_Ext::setClipRect(surf, rect * getScalingFactor());
}
~CClipRectGuard()
{
CSDL_Ext::setClipRect(surf, oldRect);
}
};
}

View File

@ -11,11 +11,13 @@
#include "StdInc.h"
#include "ScreenHandler.h"
#include "../CGameInfo.h"
#include "../CMT.h"
#include "../eventsSDL/NotificationHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CursorHandler.h"
#include "../gui/WindowHandler.h"
#include "../renderSDL/SDL_Extensions.h"
#include "CMT.h"
#include "../render/Canvas.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/constants/StringConstants.h"
@ -31,12 +33,7 @@
#include <SDL.h>
// TODO: should be made into a private members of ScreenHandler
static SDL_Window * mainWindow = nullptr;
SDL_Renderer * mainRenderer = nullptr;
SDL_Texture * screenTexture = nullptr;
SDL_Surface * screen = nullptr; //main screen surface
SDL_Surface * screen2 = nullptr; //and hlp surface (used to store not-active interfaces layer)
SDL_Surface * screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
static const std::string NAME = GameConstants::VCMI_VERSION; //application name
static constexpr Point heroes3Resolution = Point(800, 600);
@ -434,18 +431,6 @@ void ScreenHandler::initializeScreenBuffers()
throw std::runtime_error("Unable to create screen texture");
}
screen2 = CSDL_Ext::copySurface(screen);
if(nullptr == screen2)
{
throw std::runtime_error("Unable to copy surface\n");
}
if (GH.windows().count() > 1)
screenBuf = screen2;
else
screenBuf = screen;
clearScreen();
}
@ -590,15 +575,6 @@ int ScreenHandler::getPreferredRenderingDriver() const
void ScreenHandler::destroyScreenBuffers()
{
// screenBuf is not a separate surface, but points to either screen or screen2 - just set to null
screenBuf = nullptr;
if(nullptr != screen2)
{
SDL_FreeSurface(screen2);
screen2 = nullptr;
}
if(nullptr != screen)
{
SDL_FreeSurface(screen);
@ -644,6 +620,24 @@ void ScreenHandler::clearScreen()
SDL_RenderPresent(mainRenderer);
}
Canvas ScreenHandler::getScreenCanvas() const
{
return Canvas::createFromSurface(screen, CanvasScalingPolicy::AUTO);
}
void ScreenHandler::updateScreenTexture()
{
SDL_UpdateTexture(screenTexture, nullptr, screen->pixels, screen->pitch);
}
void ScreenHandler::presentScreenTexture()
{
SDL_RenderClear(mainRenderer);
SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr);
CCS->curh->render();
SDL_RenderPresent(mainRenderer);
}
std::vector<Point> ScreenHandler::getSupportedResolutions() const
{
int displayID = getPreferredDisplayIndex();

View File

@ -10,14 +10,14 @@
#pragma once
#include "../../lib/Point.h"
#include "../render/IScreenHandler.h"
struct SDL_Texture;
struct SDL_Window;
struct SDL_Renderer;
struct SDL_Surface;
#include "../../lib/Point.h"
#include "../render/IScreenHandler.h"
enum class EWindowMode
{
// game runs in a window that covers part of the screen
@ -44,6 +44,10 @@ enum class EUpscalingFilter
/// This class is responsible for management of game window and its main rendering surface
class ScreenHandler final : public IScreenHandler
{
SDL_Window * mainWindow = nullptr;
SDL_Texture * screenTexture = nullptr;
SDL_Surface * screen = nullptr;
EUpscalingFilter upscalingFilter = EUpscalingFilter::AUTO;
/// Dimensions of target surfaces/textures, this value is what game logic views as screen size
@ -114,6 +118,10 @@ public:
int getInterfaceScalingPercentage() const final;
Canvas getScreenCanvas() const final;
void updateScreenTexture() final;
void presentScreenTexture() final;
std::vector<Point> getSupportedResolutions() const final;
std::vector<Point> getSupportedResolutions(int displayIndex) const;
std::tuple<int, int> getSupportedScalingRange() const final;