more screen modes

- add support for different aspect ratios
- add support for displaying only one screen at once
This commit is contained in:
RSDuck 2021-01-24 22:32:02 +01:00
parent 536902d610
commit b9a56bc4e4
6 changed files with 314 additions and 147 deletions

View File

@ -121,21 +121,36 @@ void EnableCheats(bool enable);
// 0 = even (both screens get same size)
// 1 = emphasize top screen (make top screen as big as possible, fit bottom screen in remaining space)
// 2 = emphasize bottom screen
// 4 = top only
// 5 = bottom only
// * screenGap: size of the gap between the two screens
// * integerScale: force screens to be scaled up at integer scaling factors
// * screenSwap: whether to swap the position of both screens
void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale, int swapScreens);
// * topAspect/botAspect: ratio by which to scale the top and bottom screen respectively
void SetupScreenLayout(int screenWidth, int screenHeight,
int screenLayout,
int rotation,
int sizing,
int screenGap,
bool integerScale,
bool swapScreens,
float topAspect, float botAspect);
// get a 2x3 transform matrix for each screen
const int MaxScreenTransforms = 3;
// get a 2x3 transform matrix for each screen and whether it's a top or bottom screen
// note: the transform assumes an origin point at the top left of the display,
// X going left and Y going down
// for each screen the source coordinates should be (0,0) and (256,192)
// 'top' and 'bot' should point each to an array of 6 floats
void GetScreenTransforms(float* top, float* bot);
// 'out' should point to an array of 6*MaxScreenTransforms floats
// 'kind' should point to an array of MaxScreenTransforms ints
// (0 = indicates top screen, 1 = bottom screen)
// returns the amount of screens
int GetScreenTransforms(float* out, int* kind);
// de-transform the provided host display coordinates to get coordinates
// on the bottom screen
void GetTouchCoords(int& x, int& y);
bool GetTouchCoords(int& x, int& y);
// initialize the audio utility

View File

@ -31,7 +31,8 @@ namespace Frontend
float TopScreenMtx[6];
float BotScreenMtx[6];
float TouchMtx[6];
bool TopEnable;
bool BotEnable;
void M23_Identity(float* m)
{
@ -47,6 +48,13 @@ void M23_Scale(float* m, float s)
m[4] *= s; m[5] *= s;
}
void M23_Scale(float* m, float x, float y)
{
m[0] *= x; m[1] *= y;
m[2] *= x; m[3] *= y;
m[4] *= x; m[5] *= y;
}
void M23_RotateFast(float* m, int angle)
{
if (angle == 0) return;
@ -109,7 +117,14 @@ void M23_Transform(float* m, float& x, float& y)
}
void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale, int swapScreens)
void SetupScreenLayout(int screenWidth, int screenHeight,
int screenLayout,
int rotation,
int sizing,
int screenGap,
bool integerScale,
bool swapScreens,
float topAspect, float botAspect)
{
float refpoints[4][2] =
{
@ -130,6 +145,9 @@ void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int
M23_Translate(TopScreenMtx, -256/2, -192/2);
M23_Translate(BotScreenMtx, -256/2, -192/2);
M23_Scale(TopScreenMtx, topAspect, 1);
M23_Scale(BotScreenMtx, botAspect, 1);
// rotation
{
float rotmtx[6];
@ -145,25 +163,62 @@ void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int
M23_Transform(BotScreenMtx, refpoints[3][0], refpoints[3][1]);
}
int posRefPointOffset = 0;
int posRefPointCount = 4;
if (sizing == 4 || sizing == 5)
{
float* mtx = sizing == 4 ? TopScreenMtx : BotScreenMtx;
int primOffset = sizing == 4 ? 0 : 2;
int secOffset = sizing == 5 ? 2 : 0;
float hSize = fabsf(refpoints[primOffset][0] - refpoints[primOffset+1][0]);
float vSize = fabsf(refpoints[primOffset][1] - refpoints[primOffset+1][1]);
float scale = std::min(screenWidth / hSize, screenHeight / vSize);
if (integerScale)
scale = floorf(scale);
TopEnable = sizing == 4;
BotEnable = sizing == 5;
botScale = scale;
M23_Scale(mtx, scale);
refpoints[primOffset][0] *= scale;
refpoints[primOffset][1] *= scale;
refpoints[primOffset+1][0] *= scale;
refpoints[primOffset+1][1] *= scale;
posRefPointOffset = primOffset;
posRefPointCount = 2;
}
else
{
TopEnable = BotEnable = true;
// move screens apart
{
int idx = layout == 0 ? 1 : 0;
float offset =
(((layout == 0 && (rotation % 2 == 0)) || (layout == 1 && (rotation % 2 == 1))
? 192.f : 256.f)
+ screenGap) / 2.f;
bool moveV = rotation % 2 == layout;
float offsetTop = (moveV ? 192 : 256 * topAspect) / 2 + screenGap / 2;
float offsetBot = (moveV ? 192 : 256 * botAspect) / 2 + screenGap / 2;
if ((rotation == 1 || rotation == 2) ^ swapScreens)
offset *= -1.f;
{
offsetTop *= -1;
offsetBot *= -1;
}
M23_Translate(TopScreenMtx, (idx==0)?-offset:0, (idx==1)?-offset:0);
M23_Translate(BotScreenMtx, (idx==0)?offset:0, (idx==1)?offset:0);
M23_Translate(TopScreenMtx, (idx==0)?-offsetTop:0, (idx==1)?-offsetTop:0);
M23_Translate(BotScreenMtx, (idx==0)?offsetBot:0, (idx==1)?offsetBot:0);
refpoints[0][idx] -= offset;
refpoints[1][idx] -= offset;
refpoints[2][idx] += offset;
refpoints[3][idx] += offset;
refpoints[0][idx] -= offsetTop;
refpoints[1][idx] -= offsetTop;
refpoints[2][idx] += offsetBot;
refpoints[3][idx] += offsetBot;
botTrans[idx] = offset;
botTrans[idx] = offsetBot;
}
// scale
@ -268,13 +323,14 @@ void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int
botScale = (sizing == 1) ? secScale : primScale;
}
}
}
// position
{
float minX = refpoints[0][0], maxX = minX;
float minY = refpoints[0][1], maxY = minY;
float minX = refpoints[posRefPointOffset][0], maxX = minX;
float minY = refpoints[posRefPointOffset][1], maxY = minY;
for (int i = 1; i < 4; i++)
for (int i = posRefPointOffset + 1; i < posRefPointOffset + posRefPointCount; i++)
{
minX = std::min(minX, refpoints[i][0]);
minY = std::min(minY, refpoints[i][1]);
@ -297,6 +353,7 @@ void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int
// prepare a 'reverse' matrix for the touchscreen
// this matrix undoes the transforms applied to the bottom screen
// and can be used to calculate touchscreen coords from host screen coords
if (BotEnable)
{
M23_Identity(TouchMtx);
@ -309,18 +366,33 @@ void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int
M23_RotateFast(rotmtx, (4-rotation) & 3);
M23_Multiply(TouchMtx, rotmtx, TouchMtx);
M23_Scale(TouchMtx, 1.f/botAspect, 1);
M23_Translate(TouchMtx, 256/2, 192/2);
}
}
void GetScreenTransforms(float* top, float* bot)
int GetScreenTransforms(float* out, int* kind)
{
memcpy(top, TopScreenMtx, 6*sizeof(float));
memcpy(bot, BotScreenMtx, 6*sizeof(float));
int num = 0;
if (TopEnable)
{
memcpy(out + 6*num, TopScreenMtx, sizeof(TopScreenMtx));
kind[num] = 0;
num++;
}
if (BotEnable)
{
memcpy(out + 6*num, BotScreenMtx, sizeof(BotScreenMtx));
kind[num] = 1;
num++;
}
return num;
}
void GetTouchCoords(int& x, int& y)
bool GetTouchCoords(int& x, int& y)
{
if (BotEnable)
{
float vx = x;
float vy = y;
@ -328,6 +400,11 @@ void GetTouchCoords(int& x, int& y)
x = (int)vx;
y = (int)vy;
if (x >= 0 && x < 256 && y >= 0 && y < 192)
return true;
}
return false;
}
}

View File

@ -42,6 +42,8 @@ int ScreenLayout;
int ScreenSwap;
int ScreenSizing;
int IntegerScaling;
int ScreenAspectTop;
int ScreenAspectBot;
int ScreenFilter;
int ScreenUseGL;
@ -146,6 +148,8 @@ ConfigEntry PlatformConfigFile[] =
{"ScreenSwap", 0, &ScreenSwap, 0, NULL, 0},
{"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0},
{"IntegerScaling", 0, &IntegerScaling, 0, NULL, 0},
{"ScreenAspectTop",0, &ScreenAspectTop,0, NULL, 0},
{"ScreenAspectBot",0, &ScreenAspectBot,0, NULL, 0},
{"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0},
{"ScreenUseGL", 0, &ScreenUseGL, 0, NULL, 0},

View File

@ -56,6 +56,8 @@ extern int ScreenGap;
extern int ScreenLayout;
extern int ScreenSwap;
extern int ScreenSizing;
extern int ScreenAspectTop;
extern int ScreenAspectBot;
extern int IntegerScaling;
extern int ScreenFilter;

View File

@ -646,15 +646,25 @@ void ScreenHandler::screenSetupLayout(int w, int h)
int sizing = Config::ScreenSizing;
if (sizing == 3) sizing = autoScreenSizing;
float aspectRatios[] =
{
1.f,
(16.f/9)/(4.f/3),
(21.f/9)/(4.f/3),
((float)w/h)/(4.f/3)
};
Frontend::SetupScreenLayout(w, h,
Config::ScreenLayout,
Config::ScreenRotation,
sizing,
Config::ScreenGap,
Config::IntegerScaling != 0,
Config::ScreenSwap != 0);
Config::ScreenSwap != 0,
aspectRatios[Config::ScreenAspectTop],
aspectRatios[Config::ScreenAspectBot]);
Frontend::GetScreenTransforms(screenMatrix[0], screenMatrix[1]);
numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind);
}
QSize ScreenHandler::screenGetMinSize()
@ -779,19 +789,16 @@ void ScreenPanelNative::setupScreenLayout()
{
int w = width();
int h = height();
float* mtx;
screenSetupLayout(w, h);
mtx = screenMatrix[0];
screenTrans[0].setMatrix(mtx[0], mtx[1], 0.f,
mtx[2], mtx[3], 0.f,
mtx[4], mtx[5], 1.f);
mtx = screenMatrix[1];
screenTrans[1].setMatrix(mtx[0], mtx[1], 0.f,
for (int i = 0; i < numScreens; i++)
{
float* mtx = screenMatrix[i];
screenTrans[i].setMatrix(mtx[0], mtx[1], 0.f,
mtx[2], mtx[3], 0.f,
mtx[4], mtx[5], 1.f);
}
}
void ScreenPanelNative::paintEvent(QPaintEvent* event)
@ -811,11 +818,11 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event)
QRect screenrc(0, 0, 256, 192);
painter.setTransform(screenTrans[0]);
painter.drawImage(screenrc, screen[0]);
painter.setTransform(screenTrans[1]);
painter.drawImage(screenrc, screen[1]);
for (int i = 0; i < numScreens; i++)
{
painter.setTransform(screenTrans[i]);
painter.drawImage(screenrc, screen[screenKind[i]]);
}
OSD::Update(nullptr);
OSD::DrawNative(painter);
@ -1002,11 +1009,11 @@ void ScreenPanelGL::paintGL()
GLint transloc = screenShader->uniformLocation("uTransform");
glUniformMatrix2x3fv(transloc, 1, GL_TRUE, screenMatrix[0]);
glDrawArrays(GL_TRIANGLES, 0, 2*3);
glUniformMatrix2x3fv(transloc, 1, GL_TRUE, screenMatrix[1]);
glDrawArrays(GL_TRIANGLES, 2*3, 2*3);
for (int i = 0; i < numScreens; i++)
{
glUniformMatrix2x3fv(transloc, 1, GL_TRUE, screenMatrix[i]);
glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3);
}
screenShader->release();
@ -1272,9 +1279,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
QMenu* submenu = menu->addMenu("Screen sizing");
grpScreenSizing = new QActionGroup(submenu);
const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto"};
const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"};
for (int i = 0; i < 4; i++)
for (int i = 0; i < 6; i++)
{
actScreenSizing[i] = submenu->addAction(QString(screensizing[i]));
actScreenSizing[i]->setActionGroup(grpScreenSizing);
@ -1290,6 +1297,38 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actIntegerScaling->setCheckable(true);
connect(actIntegerScaling, &QAction::triggered, this, &MainWindow::onChangeIntegerScaling);
}
{
QMenu* submenu = menu->addMenu("Aspect ratio");
grpScreenAspectTop = new QActionGroup(submenu);
const char* aspectRatiosTop[] = {"Top 4:3 (native)", "Top 16:9", "Top 21:9", "Top window"};
for (int i = 0; i < 4; i++)
{
actScreenAspectTop[i] = submenu->addAction(QString(aspectRatiosTop[i]));
actScreenAspectTop[i]->setActionGroup(grpScreenAspectTop);
actScreenAspectTop[i]->setData(QVariant(i));
actScreenAspectTop[i]->setCheckable(true);
}
connect(grpScreenAspectTop, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectTop);
submenu->addSeparator();
grpScreenAspectBot = new QActionGroup(submenu);
const char* aspectRatiosBot[] = {"Bottom 4:3 (native)", "Bottom 16:9", "Bottom 21:9", "Bottom window"};
for (int i = 0; i < 4; i++)
{
actScreenAspectBot[i] = submenu->addAction(QString(aspectRatiosBot[i]));
actScreenAspectBot[i]->setActionGroup(grpScreenAspectBot);
actScreenAspectBot[i]->setData(QVariant(i));
actScreenAspectBot[i]->setCheckable(true);
}
connect(grpScreenAspectBot, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectBot);
}
actScreenFiltering = menu->addAction("Screen filtering");
actScreenFiltering->setCheckable(true);
@ -1352,6 +1391,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actScreenSwap->setChecked(Config::ScreenSwap != 0);
actScreenAspectTop[Config::ScreenAspectTop]->setChecked(true);
actScreenAspectBot[Config::ScreenAspectBot]->setChecked(true);
actScreenFiltering->setChecked(Config::ScreenFilter != 0);
actShowOSD->setChecked(Config::ShowOSD != 0);
@ -2264,6 +2306,22 @@ void MainWindow::onChangeScreenSizing(QAction* act)
emit screenLayoutChange();
}
void MainWindow::onChangeScreenAspectTop(QAction* act)
{
int aspect = act->data().toInt();
Config::ScreenAspectTop = aspect;
emit screenLayoutChange();
}
void MainWindow::onChangeScreenAspectBot(QAction* act)
{
int aspect = act->data().toInt();
Config::ScreenAspectBot = aspect;
emit screenLayoutChange();
}
void MainWindow::onChangeIntegerScaling(bool checked)
{
Config::IntegerScaling = checked?1:0;
@ -2444,7 +2502,9 @@ int main(int argc, char** argv)
SANITIZE(Config::ScreenRotation, 0, 3);
SANITIZE(Config::ScreenGap, 0, 500);
SANITIZE(Config::ScreenLayout, 0, 2);
SANITIZE(Config::ScreenSizing, 0, 3);
SANITIZE(Config::ScreenSizing, 0, 5);
SANITIZE(Config::ScreenAspectTop, 0, 4);
SANITIZE(Config::ScreenAspectBot, 0, 4);
#undef SANITIZE
QSurfaceFormat format;

View File

@ -34,6 +34,7 @@
#include <QOpenGLFunctions_3_2_Core>
#include <QOpenGLShaderProgram>
#include "FrontendUtil.h"
class EmuThread : public QThread
{
@ -105,7 +106,9 @@ protected:
void screenOnMouseRelease(QMouseEvent* event);
void screenOnMouseMove(QMouseEvent* event);
float screenMatrix[2][6];
float screenMatrix[Frontend::MaxScreenTransforms][6];
int screenKind[Frontend::MaxScreenTransforms];
int numScreens;
bool touching;
@ -137,7 +140,7 @@ private:
void setupScreenLayout();
QImage screen[2];
QTransform screenTrans[2];
QTransform screenTrans[Frontend::MaxScreenTransforms];
};
@ -237,6 +240,8 @@ private slots:
void onChangeScreenLayout(QAction* act);
void onChangeScreenSwap(bool checked);
void onChangeScreenSizing(QAction* act);
void onChangeScreenAspectTop(QAction* act);
void onChangeScreenAspectBot(QAction* act);
void onChangeIntegerScaling(bool checked);
void onChangeScreenFiltering(bool checked);
void onChangeShowOSD(bool checked);
@ -303,8 +308,12 @@ public:
QAction* actScreenLayout[3];
QAction* actScreenSwap;
QActionGroup* grpScreenSizing;
QAction* actScreenSizing[4];
QAction* actScreenSizing[6];
QAction* actIntegerScaling;
QActionGroup* grpScreenAspectTop;
QAction* actScreenAspectTop[4];
QActionGroup* grpScreenAspectBot;
QAction* actScreenAspectBot[4];
QAction* actScreenFiltering;
QAction* actShowOSD;
QAction* actLimitFramerate;