Added support for rotating the application screen contents 0, 90, 180 or 270 degrees.

Also improved post processing shader rendering when running in vertical orientation.
This commit is contained in:
Leon Styhre 2023-02-06 23:38:35 +01:00
parent b3c997600c
commit b33c7603bb
13 changed files with 304 additions and 123 deletions

View file

@ -71,21 +71,21 @@ void MediaViewer::render(const glm::mat4& /*parentTrans*/)
shaders = Renderer::Shader::SCANLINES; shaders = Renderer::Shader::SCANLINES;
if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) { if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) {
shaders |= Renderer::Shader::BLUR_HORIZONTAL; shaders |= Renderer::Shader::BLUR_HORIZONTAL;
float heightModifier {Renderer::getScreenHeightModifier()}; const float resolutionModifier {mRenderer->getScreenResolutionModifier()};
// clang-format off // clang-format off
if (heightModifier < 1) if (resolutionModifier < 1)
videoParameters.blurPasses = 2; // Below 1080 videoParameters.blurPasses = 2; // Below 1080
else if (heightModifier >= 4) else if (resolutionModifier >= 4)
videoParameters.blurPasses = 12; // 8K videoParameters.blurPasses = 12; // 8K
else if (heightModifier >= 2.9) else if (resolutionModifier >= 2.9)
videoParameters.blurPasses = 10; // 6K videoParameters.blurPasses = 10; // 6K
else if (heightModifier >= 2.6) else if (resolutionModifier >= 2.6)
videoParameters.blurPasses = 8; // 5K videoParameters.blurPasses = 8; // 5K
else if (heightModifier >= 2) else if (resolutionModifier >= 2)
videoParameters.blurPasses = 5; // 4K videoParameters.blurPasses = 5; // 4K
else if (heightModifier >= 1.3) else if (resolutionModifier >= 1.3)
videoParameters.blurPasses = 3; // 1440 videoParameters.blurPasses = 3; // 1440
else if (heightModifier >= 1) else if (resolutionModifier >= 1)
videoParameters.blurPasses = 2; // 1080 videoParameters.blurPasses = 2; // 1080
// clang-format on // clang-format on
} }

View file

@ -327,21 +327,21 @@ void Screensaver::renderScreensaver()
shaders = Renderer::Shader::SCANLINES; shaders = Renderer::Shader::SCANLINES;
if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) { if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) {
shaders |= Renderer::Shader::BLUR_HORIZONTAL; shaders |= Renderer::Shader::BLUR_HORIZONTAL;
float heightModifier {Renderer::getScreenHeightModifier()}; const float resolutionModifier {mRenderer->getScreenResolutionModifier()};
// clang-format off // clang-format off
if (heightModifier < 1) if (resolutionModifier < 1)
videoParameters.blurPasses = 2; // Below 1080 videoParameters.blurPasses = 2; // Below 1080
else if (heightModifier >= 4) else if (resolutionModifier >= 4)
videoParameters.blurPasses = 12; // 8K videoParameters.blurPasses = 12; // 8K
else if (heightModifier >= 2.9) else if (resolutionModifier >= 2.9)
videoParameters.blurPasses = 10; // 6K videoParameters.blurPasses = 10; // 6K
else if (heightModifier >= 2.6) else if (resolutionModifier >= 2.6)
videoParameters.blurPasses = 8; // 5K videoParameters.blurPasses = 8; // 5K
else if (heightModifier >= 2) else if (resolutionModifier >= 2)
videoParameters.blurPasses = 5; // 4K videoParameters.blurPasses = 5; // 4K
else if (heightModifier >= 1.3) else if (resolutionModifier >= 1.3)
videoParameters.blurPasses = 3; // 1440 videoParameters.blurPasses = 3; // 1440
else if (heightModifier >= 1) else if (resolutionModifier >= 1)
videoParameters.blurPasses = 2; // 1080 videoParameters.blurPasses = 2; // 1080
// clang-format on // clang-format on
} }

View file

@ -1303,6 +1303,29 @@ void GuiMenu::openOtherOptions()
} }
}); });
// Screen contents rotation.
auto screenRotate =
std::make_shared<OptionListComponent<std::string>>(getHelpStyle(), "ROTATE SCREEN", false);
const std::string& selectedScreenRotate {
std::to_string(Settings::getInstance()->getInt("ScreenRotate"))};
screenRotate->add("DISABLED", "0", selectedScreenRotate == "0");
screenRotate->add("90 DEGREES", "90", selectedScreenRotate == "90");
screenRotate->add("180 DEGREES", "180", selectedScreenRotate == "180");
screenRotate->add("270 DEGREES", "270", selectedScreenRotate == "270");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set screen rotation to "0" in this case.
if (screenRotate->getSelectedObjects().size() == 0)
screenRotate->selectEntry(0);
s->addWithLabel("ROTATE SCREEN (REQUIRES RESTART)", screenRotate);
s->addSaveFunc([screenRotate, s] {
if (screenRotate->getSelected() !=
std::to_string(Settings::getInstance()->getInt("ScreenRotate"))) {
Settings::getInstance()->setInt("ScreenRotate",
atoi(screenRotate->getSelected().c_str()));
s->setNeedsSaving();
}
});
// Keyboard quit shortcut. // Keyboard quit shortcut.
auto keyboardQuitShortcut = std::make_shared<OptionListComponent<std::string>>( auto keyboardQuitShortcut = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "KEYBOARD QUIT SHORTCUT", false); getHelpStyle(), "KEYBOARD QUIT SHORTCUT", false);

View file

@ -297,14 +297,14 @@ bool parseArgs(int argc, char* argv[])
std::cerr << "Error: No screenrotate value supplied\n"; std::cerr << "Error: No screenrotate value supplied\n";
return false; return false;
} }
std::string rotateValue {argv[i + 1]}; const std::string rotateValue {argv[i + 1]};
if (rotateValue != "on" && rotateValue != "off" && rotateValue != "1" && if (rotateValue != "0" && rotateValue != "90" && rotateValue != "180" &&
rotateValue != "0") { rotateValue != "270") {
std::cerr << "Error: Invalid screenrotate value supplied\n"; std::cerr << "Error: Invalid screenrotate value supplied\n";
return false; return false;
} }
bool screenRotate {(rotateValue == "on" || rotateValue == "1") ? true : false}; Settings::getInstance()->setInt("ScreenRotate", atoi(argv[i + 1]));
Settings::getInstance()->setBool("ScreenRotate", screenRotate); settingsNeedSaving = true;
++i; ++i;
} }
else if (strcmp(argv[i], "--vsync") == 0) { else if (strcmp(argv[i], "--vsync") == 0) {
@ -401,9 +401,9 @@ bool parseArgs(int argc, char* argv[])
"Usage: emulationstation [options]\n" "Usage: emulationstation [options]\n"
"EmulationStation Desktop Edition, Emulator Frontend\n\n" "EmulationStation Desktop Edition, Emulator Frontend\n\n"
"Options:\n" "Options:\n"
" --display [index 1-4] Display/monitor to use\n" " --display [1 to 4] Display/monitor to use\n"
" --resolution [width] [height] Application resolution\n" " --resolution [width] [height] Application resolution\n"
" --screenrotate [1/on or 0/off] Rotate application screen 180 degrees\n" " --screenrotate [0, 90, 180 or 270] Rotate screen contents within application window\n"
" --vsync [1/on or 0/off] Turn VSync on or off (default is on)\n" " --vsync [1/on or 0/off] Turn VSync on or off (default is on)\n"
" --max-vram [size] Max VRAM to use (in mebibytes) before swapping\n" " --max-vram [size] Max VRAM to use (in mebibytes) before swapping\n"
#if !defined(USE_OPENGLES) #if !defined(USE_OPENGLES)
@ -417,7 +417,7 @@ bool parseArgs(int argc, char* argv[])
" --force-full Force the UI mode to Full\n" " --force-full Force the UI mode to Full\n"
" --force-kiosk Force the UI mode to Kiosk\n" " --force-kiosk Force the UI mode to Kiosk\n"
" --force-kid Force the UI mode to Kid\n" " --force-kid Force the UI mode to Kid\n"
" --force-input-config Force configuration of input device\n" " --force-input-config Force configuration of input devices\n"
" --create-system-dirs Create game system directories\n" " --create-system-dirs Create game system directories\n"
" --home [path] Directory to use as home path\n" " --home [path] Directory to use as home path\n"
" --debug Print debug information\n" " --debug Print debug information\n"

View file

@ -31,7 +31,6 @@ namespace
"IgnoreGamelist", // --ignore-gamelist "IgnoreGamelist", // --ignore-gamelist
"SplashScreen", // --no-splash "SplashScreen", // --no-splash
"Debug", // --debug "Debug", // --debug
"ScreenRotate", // --screenrotate [1/on or 0/off]
"VSync", // --vsync [1/on or 0/off] "VSync", // --vsync [1/on or 0/off]
"ForceFull", // --force-full "ForceFull", // --force-full
"ForceKiosk", // --force-kiosk "ForceKiosk", // --force-kiosk
@ -246,10 +245,11 @@ void Settings::setDefaults()
#else #else
mIntMap["MaxVRAM"] = {512, 512}; mIntMap["MaxVRAM"] = {512, 512};
#endif #endif
mIntMap["DisplayIndex"] = {1, 1};
#if !defined(USE_OPENGLES) #if !defined(USE_OPENGLES)
mIntMap["AntiAliasing"] = {0, 0}; mIntMap["AntiAliasing"] = {0, 0};
#endif #endif
mIntMap["DisplayIndex"] = {1, 1};
mIntMap["ScreenRotate"] = {0, 0};
#if defined(__APPLE__) #if defined(__APPLE__)
mStringMap["KeyboardQuitShortcut"] = {"CmdQ", "CmdQ"}; mStringMap["KeyboardQuitShortcut"] = {"CmdQ", "CmdQ"};
#else #else
@ -295,7 +295,6 @@ void Settings::setDefaults()
mBoolMap["ForceKiosk"] = {false, false}; mBoolMap["ForceKiosk"] = {false, false};
mBoolMap["IgnoreGamelist"] = {false, false}; mBoolMap["IgnoreGamelist"] = {false, false};
mBoolMap["SplashScreen"] = {true, true}; mBoolMap["SplashScreen"] = {true, true};
mBoolMap["ScreenRotate"] = {false, false};
mBoolMap["VSync"] = {true, true}; mBoolMap["VSync"] = {true, true};
mIntMap["WindowWidth"] = {0, 0}; mIntMap["WindowWidth"] = {0, 0};
mIntMap["WindowHeight"] = {0, 0}; mIntMap["WindowHeight"] = {0, 0};

View file

@ -160,7 +160,7 @@ bool Window::init()
progressBarRect.color = DEFAULT_TEXTCOLOR; progressBarRect.color = DEFAULT_TEXTCOLOR;
mProgressBarRectangles.emplace_back(progressBarRect); mProgressBarRectangles.emplace_back(progressBarRect);
const float borderThickness {std::ceil(2.0f * mRenderer->getScreenHeightModifier())}; const float borderThickness {std::ceil(2.0f * mRenderer->getScreenResolutionModifier())};
progressBarRect.barWidth -= borderThickness * 2.0f; progressBarRect.barWidth -= borderThickness * 2.0f;
progressBarRect.barHeight -= borderThickness * 2.0f; progressBarRect.barHeight -= borderThickness * 2.0f;
@ -502,21 +502,21 @@ void Window::render()
Renderer::postProcessingParams backgroundParameters; Renderer::postProcessingParams backgroundParameters;
if (Settings::getInstance()->getBool("MenuBlurBackground")) { if (Settings::getInstance()->getBool("MenuBlurBackground")) {
float heightModifier {mRenderer->getScreenHeightModifier()}; const float resolutionModifier {mRenderer->getScreenResolutionModifier()};
// clang-format off // clang-format off
if (heightModifier < 1) if (resolutionModifier < 1)
backgroundParameters.blurPasses = 2; // Below 1080 backgroundParameters.blurPasses = 2; // Below 1080
else if (heightModifier >= 4) else if (resolutionModifier >= 4)
backgroundParameters.blurPasses = 12; // 8K backgroundParameters.blurPasses = 12; // 8K
else if (heightModifier >= 2.9) else if (resolutionModifier >= 2.9)
backgroundParameters.blurPasses = 10; // 6K backgroundParameters.blurPasses = 10; // 6K
else if (heightModifier >= 2.6) else if (resolutionModifier >= 2.6)
backgroundParameters.blurPasses = 8; // 5K backgroundParameters.blurPasses = 8; // 5K
else if (heightModifier >= 2) else if (resolutionModifier >= 2)
backgroundParameters.blurPasses = 5; // 4K backgroundParameters.blurPasses = 5; // 4K
else if (heightModifier >= 1.3) else if (resolutionModifier >= 1.3)
backgroundParameters.blurPasses = 3; // 1440 backgroundParameters.blurPasses = 3; // 1440
else if (heightModifier >= 1) else if (resolutionModifier >= 1)
backgroundParameters.blurPasses = 2; // 1080 backgroundParameters.blurPasses = 2; // 1080
// clang-format on // clang-format on
@ -535,9 +535,16 @@ void Window::render()
&processedTexture[0]); &processedTexture[0]);
} }
if (mRenderer->getScreenRotation() == 0 || mRenderer->getScreenRotation() == 180) {
mPostprocessedBackground->initFromPixels( mPostprocessedBackground->initFromPixels(
&processedTexture[0], static_cast<size_t>(mRenderer->getScreenWidth()), &processedTexture[0], static_cast<size_t>(mRenderer->getScreenWidth()),
static_cast<size_t>(mRenderer->getScreenHeight())); static_cast<size_t>(mRenderer->getScreenHeight()));
}
else {
mPostprocessedBackground->initFromPixels(
&processedTexture[0], static_cast<size_t>(mRenderer->getScreenHeight()),
static_cast<size_t>(mRenderer->getScreenWidth()));
}
mBackgroundOverlay->setImage(mPostprocessedBackground); mBackgroundOverlay->setImage(mPostprocessedBackground);

View file

@ -119,7 +119,30 @@ bool Renderer::createWindow()
mScreenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ? mScreenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ?
Settings::getInstance()->getInt("ScreenOffsetY") : Settings::getInstance()->getInt("ScreenOffsetY") :
0; 0;
mScreenRotated = Settings::getInstance()->getBool("ScreenRotate"); mScreenRotation = Settings::getInstance()->getInt("ScreenRotate");
// In case someone manually added an invalid value to es_settings.xml.
if (mScreenRotation != 0 && mScreenRotation != 90 && mScreenRotation != 180 &&
mScreenRotation != 270) {
LOG(LogWarning) << "Invalid screen rotation value " << mScreenRotation
<< " defined, changing it to 0/disabled";
mScreenRotation = 0;
}
LOG(LogInfo) << "Screen rotation: "
<< (mScreenRotation == 0 ? "disabled" :
std::to_string(mScreenRotation) + " degrees");
if (mScreenRotation == 90 || mScreenRotation == 270) {
const int tempVal {sScreenWidth};
sScreenWidth = sScreenHeight;
sScreenHeight = tempVal;
}
if (sScreenHeight > sScreenWidth)
sIsVerticalOrientation = true;
else
sIsVerticalOrientation = false;
// Prevent the application window from minimizing when switching windows (when launching // Prevent the application window from minimizing when switching windows (when launching
// games or when manually switching windows using the task switcher). // games or when manually switching windows using the task switcher).
@ -224,6 +247,11 @@ bool Renderer::createWindow()
sScreenWidthModifier = static_cast<float>(sScreenWidth) / 1920.0f; sScreenWidthModifier = static_cast<float>(sScreenWidth) / 1920.0f;
sScreenAspectRatio = static_cast<float>(sScreenWidth) / static_cast<float>(sScreenHeight); sScreenAspectRatio = static_cast<float>(sScreenWidth) / static_cast<float>(sScreenHeight);
if (sIsVerticalOrientation)
sScreenResolutionModifier = sScreenWidth / 1080.0f;
else
sScreenResolutionModifier = sScreenHeight / 1080.0f;
LOG(LogInfo) << "Setting up OpenGL..."; LOG(LogInfo) << "Setting up OpenGL...";
if (!createContext()) if (!createContext())
@ -265,18 +293,41 @@ bool Renderer::init()
viewport.y = mWindowHeight - mScreenOffsetY - sScreenHeight; viewport.y = mWindowHeight - mScreenOffsetY - sScreenHeight;
viewport.w = sScreenWidth; viewport.w = sScreenWidth;
viewport.h = sScreenHeight; viewport.h = sScreenHeight;
projection = glm::ortho(0.0f, static_cast<float>(sScreenHeight),
static_cast<float>(sScreenWidth), 0.0f, -1.0f, 1.0f);
if (mScreenRotation == 0) {
mProjectionMatrix = glm::ortho(0.0f, static_cast<float>(sScreenWidth),
static_cast<float>(sScreenHeight), 0.0f, -1.0f, 1.0f);
}
else if (mScreenRotation == 90) {
projection = glm::ortho(0.0f, static_cast<float>(sScreenHeight),
static_cast<float>(sScreenWidth), 0.0f, -1.0f, 1.0f);
projection = glm::rotate(projection, glm::radians(90.0f), {0.0f, 0.0f, 1.0f});
mProjectionMatrix = glm::translate(projection, {0.0f, sScreenHeight * -1.0f, 0.0f});
}
else if (mScreenRotation == 180) {
projection = glm::ortho(0.0f, static_cast<float>(sScreenWidth), projection = glm::ortho(0.0f, static_cast<float>(sScreenWidth),
static_cast<float>(sScreenHeight), 0.0f, -1.0f, 1.0f); static_cast<float>(sScreenHeight), 0.0f, -1.0f, 1.0f);
projection = glm::rotate(projection, glm::radians(180.0f), {0.0f, 0.0f, 1.0f}); projection = glm::rotate(projection, glm::radians(180.0f), {0.0f, 0.0f, 1.0f});
mProjectionMatrixRotated = mProjectionMatrix =
glm::translate(projection, {sScreenWidth * -1.0f, sScreenHeight * -1.0f, 0.0f}); glm::translate(projection, {sScreenWidth * -1.0f, sScreenHeight * -1.0f, 0.0f});
}
else if (mScreenRotation == 270) {
projection = glm::ortho(0.0f, static_cast<float>(sScreenHeight),
static_cast<float>(sScreenWidth), 0.0f, -1.0f, 1.0f);
projection = glm::rotate(projection, glm::radians(270.0f), {0.0f, 0.0f, 1.0f});
mProjectionMatrix = glm::translate(projection, {sScreenWidth * -1.0f, 0.0f, 0.0f});
}
mProjectionMatrixNormal = glm::ortho(0.0f, static_cast<float>(sScreenWidth),
static_cast<float>(sScreenHeight), 0.0f, -1.0f, 1.0f);
viewport.x = mScreenOffsetX; viewport.x = mScreenOffsetX;
viewport.y = mScreenOffsetY; viewport.y = mScreenOffsetY;
viewport.w = sScreenWidth; viewport.w = sScreenWidth;
viewport.h = sScreenHeight; viewport.h = sScreenHeight;
mProjectionMatrix = glm::ortho(0.0f, static_cast<float>(sScreenWidth),
static_cast<float>(sScreenHeight), 0.0f, -1.0f, 1.0f);
// This is required to avoid a brief white screen flash during startup on some systems. // This is required to avoid a brief white screen flash during startup on some systems.
drawRect(0.0f, 0.0f, static_cast<float>(getScreenWidth()), drawRect(0.0f, 0.0f, static_cast<float>(getScreenWidth()),
@ -301,12 +352,20 @@ void Renderer::pushClipRect(const glm::ivec2& pos, const glm::ivec2& size)
if (box.h == 0) if (box.h == 0)
box.h = sScreenHeight - box.y; box.h = sScreenHeight - box.y;
if (mScreenRotated) { if (mScreenRotation == 0) {
box = Rect {mWindowWidth - mScreenOffsetX - box.x - box.w, box = {mScreenOffsetX + box.x, mScreenOffsetY + box.y, box.w, box.h};
mWindowHeight - mScreenOffsetY - box.y - box.h, box.w, box.h};
} }
else { else if (mScreenRotation == 90) {
box = Rect {mScreenOffsetX + box.x, mScreenOffsetY + box.y, box.w, box.h}; box = {mScreenOffsetX + mWindowWidth - (box.y + box.h), mScreenOffsetY + box.x, box.h,
box.w};
}
else if (mScreenRotation == 270) {
box = {mScreenOffsetX + box.y, mScreenOffsetY + mWindowHeight - (box.x + box.w), box.h,
box.w};
}
else if (mScreenRotation == 180) {
box = {mWindowWidth - mScreenOffsetX - box.x - box.w,
mWindowHeight - mScreenOffsetY - box.y - box.h, box.w, box.h};
} }
// Make sure the box fits within mClipStack.top(), and clip further accordingly. // Make sure the box fits within mClipStack.top(), and clip further accordingly.
@ -328,7 +387,6 @@ void Renderer::pushClipRect(const glm::ivec2& pos, const glm::ivec2& size)
box.h = 0; box.h = 0;
mClipStack.push(box); mClipStack.push(box);
setScissor(box); setScissor(box);
} }

View file

@ -52,7 +52,8 @@ public:
PREMULTIPLIED = 0x00000001, PREMULTIPLIED = 0x00000001,
FONT_TEXTURE = 0x00000002, FONT_TEXTURE = 0x00000002,
POST_PROCESSING = 0x00000004, POST_PROCESSING = 0x00000004,
CLIPPING = 0x00000008 CLIPPING = 0x00000008,
ROTATED = 0x00000010 // Screen rotated 90 or 270 degrees.
}; };
// clang-format on // clang-format on
@ -155,16 +156,11 @@ public:
const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA, const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA,
const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA); const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA);
const glm::mat4& getProjectionMatrix() const glm::mat4& getProjectionMatrix() { return mProjectionMatrix; }
{ const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrixNormal; }
if (mScreenRotated)
return mProjectionMatrixRotated;
else
return mProjectionMatrix;
}
const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrix; }
SDL_Window* getSDLWindow() { return mSDLWindow; } SDL_Window* getSDLWindow() { return mSDLWindow; }
const bool getScreenRotated() { return mScreenRotated; } const int getScreenRotation() { return mScreenRotation; }
const bool getIsVerticalOrientation() { return sIsVerticalOrientation; }
const float getWindowWidth() { return static_cast<float>(mWindowWidth); } const float getWindowWidth() { return static_cast<float>(mWindowWidth); }
const float getWindowHeight() { return static_cast<float>(mWindowHeight); } const float getWindowHeight() { return static_cast<float>(mWindowHeight); }
static const float getScreenWidth() { return static_cast<float>(sScreenWidth); } static const float getScreenWidth() { return static_cast<float>(sScreenWidth); }
@ -172,6 +168,7 @@ public:
static const float getScreenWidthModifier() { return sScreenWidthModifier; } static const float getScreenWidthModifier() { return sScreenWidthModifier; }
static const float getScreenHeightModifier() { return sScreenHeightModifier; } static const float getScreenHeightModifier() { return sScreenHeightModifier; }
static const float getScreenAspectRatio() { return sScreenAspectRatio; } static const float getScreenAspectRatio() { return sScreenAspectRatio; }
static const float getScreenResolutionModifier() { return sScreenResolutionModifier; }
static constexpr glm::mat4 getIdentity() { return glm::mat4 {1.0f}; } static constexpr glm::mat4 getIdentity() { return glm::mat4 {1.0f}; }
glm::mat4 mTrans {getIdentity()}; glm::mat4 mTrans {getIdentity()};
@ -215,19 +212,21 @@ private:
std::stack<Rect> mClipStack; std::stack<Rect> mClipStack;
SDL_Window* mSDLWindow {nullptr}; SDL_Window* mSDLWindow {nullptr};
glm::mat4 mProjectionMatrix {}; glm::mat4 mProjectionMatrix {};
glm::mat4 mProjectionMatrixRotated {}; glm::mat4 mProjectionMatrixNormal {};
int mWindowWidth {0}; int mWindowWidth {0};
int mWindowHeight {0}; int mWindowHeight {0};
static inline int sScreenWidth {0}; static inline int sScreenWidth {0};
static inline int sScreenHeight {0}; static inline int sScreenHeight {0};
int mScreenOffsetX {0}; int mScreenOffsetX {0};
int mScreenOffsetY {0}; int mScreenOffsetY {0};
bool mScreenRotated {0}; int mScreenRotation {0};
bool mInitialCursorState {1}; bool mInitialCursorState {true};
static inline bool sIsVerticalOrientation {false};
// Screen resolution modifiers relative to the 1920x1080 reference. // Screen resolution modifiers relative to the 1920x1080 reference.
static inline float sScreenHeightModifier {0.0f}; static inline float sScreenHeightModifier {0.0f};
static inline float sScreenWidthModifier {0.0f}; static inline float sScreenWidthModifier {0.0f};
static inline float sScreenAspectRatio {0.0f}; static inline float sScreenAspectRatio {0.0f};
static inline float sScreenResolutionModifier {0.0f};
}; };
#endif // ES_CORE_RENDERER_RENDERER_H #endif // ES_CORE_RENDERER_RENDERER_H

View file

@ -269,13 +269,22 @@ bool RendererOpenGL::createContext()
uint8_t data[4] {255, 255, 255, 255}; uint8_t data[4] {255, 255, 255, 255};
mWhiteTexture = createTexture(TextureType::BGRA, false, false, false, true, 1, 1, data); mWhiteTexture = createTexture(TextureType::BGRA, false, false, false, true, 1, 1, data);
mPostProcTexture1 = createTexture(TextureType::BGRA, false, false, false, false, unsigned int textureWidth {0};
static_cast<unsigned int>(getScreenWidth()), unsigned int textureHeight {0};
static_cast<unsigned int>(getScreenHeight()), nullptr);
mPostProcTexture2 = createTexture(TextureType::BGRA, false, false, false, false, if (getScreenRotation() == 0 || getScreenRotation() == 180) {
static_cast<unsigned int>(getScreenWidth()), textureWidth = static_cast<unsigned int>(getScreenWidth());
static_cast<unsigned int>(getScreenHeight()), nullptr); textureHeight = static_cast<unsigned int>(getScreenHeight());
}
else {
textureWidth = static_cast<unsigned int>(getScreenHeight());
textureHeight = static_cast<unsigned int>(getScreenWidth());
}
mPostProcTexture1 = createTexture(TextureType::BGRA, false, false, false, false, textureWidth,
textureHeight, nullptr);
mPostProcTexture2 = createTexture(TextureType::BGRA, false, false, false, false, textureWidth,
textureHeight, nullptr);
// Attach textures to the shader framebuffers. // Attach textures to the shader framebuffers.
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1));
@ -494,6 +503,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices,
GL_DYNAMIC_DRAW)); GL_DYNAMIC_DRAW));
mBlurHorizontalShader->setTextureSize({width, height}); mBlurHorizontalShader->setTextureSize({width, height});
mBlurHorizontalShader->setFlags(vertices->shaderFlags);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
mLastShader = mBlurHorizontalShader; mLastShader = mBlurHorizontalShader;
} }
@ -511,6 +521,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices,
GL_DYNAMIC_DRAW)); GL_DYNAMIC_DRAW));
mBlurVerticalShader->setTextureSize({width, height}); mBlurVerticalShader->setTextureSize({width, height});
mBlurVerticalShader->setFlags(vertices->shaderFlags);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
mLastShader = mBlurVerticalShader; mLastShader = mBlurVerticalShader;
} }
@ -521,12 +532,12 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
mScanlinelShader = getShaderProgram(Shader::SCANLINES); mScanlinelShader = getShaderProgram(Shader::SCANLINES);
float shaderWidth {width * 1.2f}; float shaderWidth {width * 1.2f};
// Scale the scanlines relative to screen resolution. // Scale the scanlines relative to screen resolution.
float screenHeightModifier {getScreenHeightModifier()}; float resolutionModifier {getScreenResolutionModifier()};
float relativeHeight {height / getScreenHeight()}; float relativeHeight {height / getScreenHeight()};
float shaderHeight {0.0f}; float shaderHeight {0.0f};
if (relativeHeight == 1.0f) { if (relativeHeight == 1.0f) {
// Full screen. // Full screen.
float modifier {1.30f - (0.1f * screenHeightModifier)}; float modifier {1.30f - (0.1f * resolutionModifier)};
shaderHeight = height * modifier; shaderHeight = height * modifier;
} }
else { else {
@ -535,7 +546,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
// scanlines to videos with non-standard aspect ratios. // scanlines to videos with non-standard aspect ratios.
float relativeWidth {width / getScreenWidth()}; float relativeWidth {width / getScreenWidth()};
float relativeAdjustment {(relativeWidth + relativeHeight) / 2.0f}; float relativeAdjustment {(relativeWidth + relativeHeight) / 2.0f};
float modifier {1.41f + relativeAdjustment / 7.0f - (0.14f * screenHeightModifier)}; float modifier {1.41f + relativeAdjustment / 7.0f - (0.14f * resolutionModifier)};
shaderHeight = height * modifier; shaderHeight = height * modifier;
} }
if (mScanlinelShader) { if (mScanlinelShader) {
@ -550,6 +561,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
mScanlinelShader->setBrightness(vertices->brightness); mScanlinelShader->setBrightness(vertices->brightness);
mScanlinelShader->setSaturation(vertices->saturation); mScanlinelShader->setSaturation(vertices->saturation);
mScanlinelShader->setTextureSize({shaderWidth, shaderHeight}); mScanlinelShader->setTextureSize({shaderWidth, shaderHeight});
mScanlinelShader->setFlags(vertices->shaderFlags);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
mLastShader = mScanlinelShader; mLastShader = mScanlinelShader;
} }
@ -566,6 +578,7 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
float heightf {getScreenHeight()}; float heightf {getScreenHeight()};
GLuint width {static_cast<GLuint>(widthf)}; GLuint width {static_cast<GLuint>(widthf)};
GLuint height {static_cast<GLuint>(heightf)}; GLuint height {static_cast<GLuint>(heightf)};
const int screenRotation {getScreenRotation()};
// Set vertex positions and texture coordinates to full screen as all // Set vertex positions and texture coordinates to full screen as all
// post-processing is applied to the complete screen area. // post-processing is applied to the complete screen area.
@ -581,6 +594,9 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
vertices->dimming = parameters.dimming; vertices->dimming = parameters.dimming;
vertices->shaderFlags = ShaderFlags::POST_PROCESSING | ShaderFlags::PREMULTIPLIED; vertices->shaderFlags = ShaderFlags::POST_PROCESSING | ShaderFlags::PREMULTIPLIED;
if (screenRotation == 90 || screenRotation == 270)
vertices->shaderFlags |= ShaderFlags::ROTATED;
if (shaders & Shader::CORE) if (shaders & Shader::CORE)
shaderList.push_back(Shader::CORE); shaderList.push_back(Shader::CORE);
if (shaders & Shader::BLUR_HORIZONTAL) if (shaders & Shader::BLUR_HORIZONTAL)
@ -595,16 +611,52 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1));
// Blit the screen contents to mPostProcTexture. int shaderCalls {0};
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, bool evenBlurPasses {true};
GL_NEAREST));
for (size_t i {0}; i < shaderList.size(); ++i) {
if (shaderList[i] == Renderer::Shader::BLUR_HORIZONTAL ||
shaderList[i] == Renderer::Shader::BLUR_VERTICAL) {
shaderCalls += parameters.blurPasses;
if (parameters.blurPasses % 2 != 0)
evenBlurPasses = false;
}
else {
++shaderCalls;
}
}
// Blit the screen contents to mPostProcTexture.
if (screenRotation == 0) {
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
}
else if (screenRotation == 90 || screenRotation == 270) {
if (!evenBlurPasses || !textureRGBA)
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, height, width, 0, 0, height, width,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
else
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, height, width, height, width, 0, 0,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
// If not rendering to a texture, apply shaders without any rotation applied.
if (!textureRGBA)
mTrans = getProjectionMatrixNormal() * getIdentity();
}
else {
if ((shaderCalls + (textureRGBA ? 1 : 0)) % 2 == 0)
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
else
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, width, height, 0, 0,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
}
if (shaderCalls > 1)
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO2)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO2));
bool firstFBO {true}; bool firstFBO {true};
int drawCalls {0};
for (size_t i = 0; i < shaderList.size(); ++i) { for (size_t i {0}; i < shaderList.size(); ++i) {
vertices->shaders = shaderList[i]; vertices->shaders = shaderList[i];
int shaderPasses {1}; int shaderPasses {1};
// For the blur shaders there is an optional variable to set the number of passes // For the blur shaders there is an optional variable to set the number of passes
@ -614,27 +666,17 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
shaderPasses = parameters.blurPasses; shaderPasses = parameters.blurPasses;
} }
for (int p = 0; p < shaderPasses; ++p) { for (int p {0}; p < shaderPasses; ++p) {
if (textureRGBA == nullptr && i == shaderList.size() - 1 && p == shaderPasses - 1) { if (!textureRGBA && i == shaderList.size() - 1 && p == shaderPasses - 1) {
// If the screen is rotated and we're at an even number of drawcalls, then
// set the projection to a non-rotated state before making the last drawcall
// as the image would otherwise get rendered upside down.
if (getScreenRotated() && drawCalls % 2 == 0) {
mTrans = getIdentity();
mTrans[3] = glm::round(mTrans[3]);
mTrans = getProjectionMatrixNormal() * mTrans;
}
// If it's the last shader pass, then render directly to the default framebuffer
// to avoid having to make an expensive glBlitFramebuffer() call.
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
drawTriangleStrips(vertices, 4, BlendFactor::SRC_ALPHA, drawTriangleStrips(vertices, 4, BlendFactor::SRC_ALPHA,
BlendFactor::ONE_MINUS_SRC_ALPHA); BlendFactor::ONE_MINUS_SRC_ALPHA);
break; break;
} }
// Apply/render the shaders.
drawTriangleStrips(vertices, 4, BlendFactor::SRC_ALPHA, drawTriangleStrips(vertices, 4, BlendFactor::SRC_ALPHA,
BlendFactor::ONE_MINUS_SRC_ALPHA); BlendFactor::ONE_MINUS_SRC_ALPHA);
++drawCalls;
if (firstFBO) { if (firstFBO) {
bindTexture(mPostProcTexture2); bindTexture(mPostProcTexture2);
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO2)); GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO2));
@ -663,16 +705,22 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO2)); GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO2));
#if defined(USE_OPENGLES) #if defined(USE_OPENGLES)
if (screenRotation == 0 || screenRotation == 180)
GL_CHECK_ERROR( GL_CHECK_ERROR(
glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, textureRGBA)); glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, textureRGBA));
else
GL_CHECK_ERROR(
glReadPixels(0, 0, height, width, GL_BGRA_EXT, GL_UNSIGNED_BYTE, textureRGBA));
#else #else
GL_CHECK_ERROR(glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, textureRGBA)); if (screenRotation == 0 || screenRotation == 180)
GL_CHECK_ERROR(
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, textureRGBA));
else
GL_CHECK_ERROR(
glReadPixels(0, 0, height, width, GL_BGRA, GL_UNSIGNED_BYTE, textureRGBA));
#endif #endif
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
} }
if (getScreenRotated())
setMatrix(getIdentity());
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
} }

View file

@ -30,15 +30,28 @@ void main()
precision mediump float; precision mediump float;
#endif #endif
uniform uint shaderFlags;
uniform vec2 textureSize; uniform vec2 textureSize;
uniform sampler2D textureSampler; uniform sampler2D textureSampler;
in vec2 texCoord; in vec2 texCoord;
out vec4 FragColor; out vec4 FragColor;
#define SourceSize vec4(textureSize, 1.0 / textureSize) // shaderFlags:
// 0x00000001 - Premultiplied alpha (BGRA)
// 0x00000002 - Font texture
// 0x00000004 - Post processing
// 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees
void main() void main()
{ {
vec4 SourceSize;
if (0x0u != (shaderFlags & 0x10u))
SourceSize = vec4(textureSize.yx, 1.0 / textureSize.yx);
else
SourceSize = vec4(textureSize.xy, 1.0 / textureSize.xy);
vec2 PIXEL_SIZE = vec2(SourceSize.z, SourceSize.w); vec2 PIXEL_SIZE = vec2(SourceSize.z, SourceSize.w);
float sampleOffsets[5] = float[5](0.0, 1.4347826, 3.3478260, 5.2608695, 7.1739130); float sampleOffsets[5] = float[5](0.0, 1.4347826, 3.3478260, 5.2608695, 7.1739130);

View file

@ -30,15 +30,28 @@ void main()
precision mediump float; precision mediump float;
#endif #endif
uniform uint shaderFlags;
uniform vec2 textureSize; uniform vec2 textureSize;
uniform sampler2D textureSampler; uniform sampler2D textureSampler;
in vec2 texCoord; in vec2 texCoord;
out vec4 FragColor; out vec4 FragColor;
#define SourceSize vec4(textureSize, 1.0 / textureSize) // shaderFlags:
// 0x00000001 - Premultiplied alpha (BGRA)
// 0x00000002 - Font texture
// 0x00000004 - Post processing
// 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees
void main() void main()
{ {
vec4 SourceSize;
if (0x0u != (shaderFlags & 0x10u))
SourceSize = vec4(textureSize.yx, 1.0 / textureSize.yx);
else
SourceSize = vec4(textureSize.xy, 1.0 / textureSize.xy);
vec2 PIXEL_SIZE = vec2(SourceSize.z, SourceSize.w); vec2 PIXEL_SIZE = vec2(SourceSize.z, SourceSize.w);
float sampleOffsets[5] = float[5](0.0, 1.4347826, 3.3478260, 5.2608695, 7.1739130); float sampleOffsets[5] = float[5](0.0, 1.4347826, 3.3478260, 5.2608695, 7.1739130);

View file

@ -54,6 +54,7 @@ out vec4 FragColor;
// 0x00000002 - Font texture // 0x00000002 - Font texture
// 0x00000004 - Post processing // 0x00000004 - Post processing
// 0x00000008 - Clipping // 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees
void main() void main()
{ {

View file

@ -60,6 +60,7 @@ uniform vec2 textureSize;
uniform float opacity; uniform float opacity;
uniform float brightness; uniform float brightness;
uniform float saturation; uniform float saturation;
uniform uint shaderFlags;
uniform sampler2D textureSampler; uniform sampler2D textureSampler;
in vec2 texCoord; in vec2 texCoord;
in vec2 onex; in vec2 onex;
@ -67,8 +68,6 @@ in vec2 oney;
in vec4 colorShift; in vec4 colorShift;
out vec4 FragColor; out vec4 FragColor;
#define SourceSize vec4(textureSize, 1.0 / textureSize)
#ifdef PARAMETER_UNIFORM #ifdef PARAMETER_UNIFORM
uniform float SPOT_WIDTH; uniform float SPOT_WIDTH;
uniform float SPOT_HEIGHT; uniform float SPOT_HEIGHT;
@ -95,8 +94,25 @@ uniform float OutputGamma;
w = 1.0 - w * w; \ w = 1.0 - w * w; \
w = w * w; w = w * w;
// shaderFlags:
// 0x00000001 - Premultiplied alpha (BGRA)
// 0x00000002 - Font texture
// 0x00000004 - Post processing
// 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees
void main() void main()
{ {
bool rotated = false;
if (0x0u != (shaderFlags & 0x10u))
rotated = true;
vec4 SourceSize;
if (rotated)
SourceSize = vec4(textureSize.yx, 1.0 / textureSize.yx);
else
SourceSize = vec4(textureSize.xy, 1.0 / textureSize.xy);
vec2 coords = (texCoord * SourceSize.xy); vec2 coords = (texCoord * SourceSize.xy);
vec2 pixel_center = floor(coords) + vec2(0.5, 0.5); vec2 pixel_center = floor(coords) + vec2(0.5, 0.5);
vec2 texture_coords = pixel_center * SourceSize.zw; vec2 texture_coords = pixel_center * SourceSize.zw;
@ -127,7 +143,11 @@ void main()
color = color + colorNB * vec4(h_weight_01); color = color + colorNB * vec4(h_weight_01);
// Vertical blending. // Vertical blending.
float dy = coords.y - pixel_center.y; float dy;
if (rotated)
dy = coords.x - pixel_center.x;
else
dy = coords.y - pixel_center.y;
float v_weight_00 = dy / SPOT_HEIGHT; float v_weight_00 = dy / SPOT_HEIGHT;
WEIGHT(v_weight_00); WEIGHT(v_weight_00);
color *= vec4(v_weight_00); color *= vec4(v_weight_00);