From b33c7603bbbd245f17bec227b6d27b03dcb7f4cf Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 6 Feb 2023 23:38:35 +0100 Subject: [PATCH] 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. --- es-app/src/MediaViewer.cpp | 16 +-- es-app/src/Screensaver.cpp | 16 +-- es-app/src/guis/GuiMenu.cpp | 23 ++++ es-app/src/main.cpp | 50 ++++----- es-core/src/Settings.cpp | 5 +- es-core/src/Window.cpp | 31 +++-- es-core/src/renderers/Renderer.cpp | 86 +++++++++++--- es-core/src/renderers/Renderer.h | 25 ++--- es-core/src/renderers/RendererOpenGL.cpp | 118 ++++++++++++++------ resources/shaders/glsl/blur_horizontal.glsl | 15 ++- resources/shaders/glsl/blur_vertical.glsl | 15 ++- resources/shaders/glsl/core.glsl | 1 + resources/shaders/glsl/scanlines.glsl | 26 ++++- 13 files changed, 304 insertions(+), 123 deletions(-) diff --git a/es-app/src/MediaViewer.cpp b/es-app/src/MediaViewer.cpp index 26f378ed6..f0abb7c51 100644 --- a/es-app/src/MediaViewer.cpp +++ b/es-app/src/MediaViewer.cpp @@ -71,21 +71,21 @@ void MediaViewer::render(const glm::mat4& /*parentTrans*/) shaders = Renderer::Shader::SCANLINES; if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) { shaders |= Renderer::Shader::BLUR_HORIZONTAL; - float heightModifier {Renderer::getScreenHeightModifier()}; + const float resolutionModifier {mRenderer->getScreenResolutionModifier()}; // clang-format off - if (heightModifier < 1) + if (resolutionModifier < 1) videoParameters.blurPasses = 2; // Below 1080 - else if (heightModifier >= 4) + else if (resolutionModifier >= 4) videoParameters.blurPasses = 12; // 8K - else if (heightModifier >= 2.9) + else if (resolutionModifier >= 2.9) videoParameters.blurPasses = 10; // 6K - else if (heightModifier >= 2.6) + else if (resolutionModifier >= 2.6) videoParameters.blurPasses = 8; // 5K - else if (heightModifier >= 2) + else if (resolutionModifier >= 2) videoParameters.blurPasses = 5; // 4K - else if (heightModifier >= 1.3) + else if (resolutionModifier >= 1.3) videoParameters.blurPasses = 3; // 1440 - else if (heightModifier >= 1) + else if (resolutionModifier >= 1) videoParameters.blurPasses = 2; // 1080 // clang-format on } diff --git a/es-app/src/Screensaver.cpp b/es-app/src/Screensaver.cpp index 71ff74702..bbbf9f09f 100644 --- a/es-app/src/Screensaver.cpp +++ b/es-app/src/Screensaver.cpp @@ -327,21 +327,21 @@ void Screensaver::renderScreensaver() shaders = Renderer::Shader::SCANLINES; if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) { shaders |= Renderer::Shader::BLUR_HORIZONTAL; - float heightModifier {Renderer::getScreenHeightModifier()}; + const float resolutionModifier {mRenderer->getScreenResolutionModifier()}; // clang-format off - if (heightModifier < 1) + if (resolutionModifier < 1) videoParameters.blurPasses = 2; // Below 1080 - else if (heightModifier >= 4) + else if (resolutionModifier >= 4) videoParameters.blurPasses = 12; // 8K - else if (heightModifier >= 2.9) + else if (resolutionModifier >= 2.9) videoParameters.blurPasses = 10; // 6K - else if (heightModifier >= 2.6) + else if (resolutionModifier >= 2.6) videoParameters.blurPasses = 8; // 5K - else if (heightModifier >= 2) + else if (resolutionModifier >= 2) videoParameters.blurPasses = 5; // 4K - else if (heightModifier >= 1.3) + else if (resolutionModifier >= 1.3) videoParameters.blurPasses = 3; // 1440 - else if (heightModifier >= 1) + else if (resolutionModifier >= 1) videoParameters.blurPasses = 2; // 1080 // clang-format on } diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 31e963417..80ff03b0b 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -1303,6 +1303,29 @@ void GuiMenu::openOtherOptions() } }); + // Screen contents rotation. + auto screenRotate = + std::make_shared>(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. auto keyboardQuitShortcut = std::make_shared>( getHelpStyle(), "KEYBOARD QUIT SHORTCUT", false); diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index 07a06d2ed..a544770d6 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -297,14 +297,14 @@ bool parseArgs(int argc, char* argv[]) std::cerr << "Error: No screenrotate value supplied\n"; return false; } - std::string rotateValue {argv[i + 1]}; - if (rotateValue != "on" && rotateValue != "off" && rotateValue != "1" && - rotateValue != "0") { + const std::string rotateValue {argv[i + 1]}; + if (rotateValue != "0" && rotateValue != "90" && rotateValue != "180" && + rotateValue != "270") { std::cerr << "Error: Invalid screenrotate value supplied\n"; return false; } - bool screenRotate {(rotateValue == "on" || rotateValue == "1") ? true : false}; - Settings::getInstance()->setBool("ScreenRotate", screenRotate); + Settings::getInstance()->setInt("ScreenRotate", atoi(argv[i + 1])); + settingsNeedSaving = true; ++i; } else if (strcmp(argv[i], "--vsync") == 0) { @@ -401,28 +401,28 @@ bool parseArgs(int argc, char* argv[]) "Usage: emulationstation [options]\n" "EmulationStation Desktop Edition, Emulator Frontend\n\n" "Options:\n" -" --display [index 1-4] Display/monitor to use\n" -" --resolution [width] [height] Application resolution\n" -" --screenrotate [1/on or 0/off] Rotate application screen 180 degrees\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" +" --display [1 to 4] Display/monitor to use\n" +" --resolution [width] [height] Application resolution\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" +" --max-vram [size] Max VRAM to use (in mebibytes) before swapping\n" #if !defined(USE_OPENGLES) -" --anti-aliasing [0, 2 or 4] Set MSAA anti-aliasing to disabled, 2x or 4x\n" +" --anti-aliasing [0, 2 or 4] Set MSAA anti-aliasing to disabled, 2x or 4x\n" #endif -" --no-splash Don't show the splash screen during startup\n" -" --gamelist-only Skip automatic game ROM search, only read from gamelist.xml\n" -" --ignore-gamelist Ignore the gamelist.xml files (useful for troubleshooting)\n" -" --show-hidden-files Show hidden files and folders\n" -" --show-hidden-games Show hidden games\n" -" --force-full Force the UI mode to Full\n" -" --force-kiosk Force the UI mode to Kiosk\n" -" --force-kid Force the UI mode to Kid\n" -" --force-input-config Force configuration of input device\n" -" --create-system-dirs Create game system directories\n" -" --home [path] Directory to use as home path\n" -" --debug Print debug information\n" -" --version, -v Display version information\n" -" --help, -h Summon a sentient, angry tuba\n"; +" --no-splash Don't show the splash screen during startup\n" +" --gamelist-only Skip automatic game ROM search, only read from gamelist.xml\n" +" --ignore-gamelist Ignore the gamelist.xml files (useful for troubleshooting)\n" +" --show-hidden-files Show hidden files and folders\n" +" --show-hidden-games Show hidden games\n" +" --force-full Force the UI mode to Full\n" +" --force-kiosk Force the UI mode to Kiosk\n" +" --force-kid Force the UI mode to Kid\n" +" --force-input-config Force configuration of input devices\n" +" --create-system-dirs Create game system directories\n" +" --home [path] Directory to use as home path\n" +" --debug Print debug information\n" +" --version, -v Display version information\n" +" --help, -h Summon a sentient, angry tuba\n"; // clang-format on return false; // Exit after printing help. } diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index c384d05e3..8843a5f74 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -31,7 +31,6 @@ namespace "IgnoreGamelist", // --ignore-gamelist "SplashScreen", // --no-splash "Debug", // --debug - "ScreenRotate", // --screenrotate [1/on or 0/off] "VSync", // --vsync [1/on or 0/off] "ForceFull", // --force-full "ForceKiosk", // --force-kiosk @@ -246,10 +245,11 @@ void Settings::setDefaults() #else mIntMap["MaxVRAM"] = {512, 512}; #endif - mIntMap["DisplayIndex"] = {1, 1}; #if !defined(USE_OPENGLES) mIntMap["AntiAliasing"] = {0, 0}; #endif + mIntMap["DisplayIndex"] = {1, 1}; + mIntMap["ScreenRotate"] = {0, 0}; #if defined(__APPLE__) mStringMap["KeyboardQuitShortcut"] = {"CmdQ", "CmdQ"}; #else @@ -295,7 +295,6 @@ void Settings::setDefaults() mBoolMap["ForceKiosk"] = {false, false}; mBoolMap["IgnoreGamelist"] = {false, false}; mBoolMap["SplashScreen"] = {true, true}; - mBoolMap["ScreenRotate"] = {false, false}; mBoolMap["VSync"] = {true, true}; mIntMap["WindowWidth"] = {0, 0}; mIntMap["WindowHeight"] = {0, 0}; diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index e5b4c9ed9..889154311 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -160,7 +160,7 @@ bool Window::init() progressBarRect.color = DEFAULT_TEXTCOLOR; 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.barHeight -= borderThickness * 2.0f; @@ -502,21 +502,21 @@ void Window::render() Renderer::postProcessingParams backgroundParameters; if (Settings::getInstance()->getBool("MenuBlurBackground")) { - float heightModifier {mRenderer->getScreenHeightModifier()}; + const float resolutionModifier {mRenderer->getScreenResolutionModifier()}; // clang-format off - if (heightModifier < 1) + if (resolutionModifier < 1) backgroundParameters.blurPasses = 2; // Below 1080 - else if (heightModifier >= 4) + else if (resolutionModifier >= 4) backgroundParameters.blurPasses = 12; // 8K - else if (heightModifier >= 2.9) + else if (resolutionModifier >= 2.9) backgroundParameters.blurPasses = 10; // 6K - else if (heightModifier >= 2.6) + else if (resolutionModifier >= 2.6) backgroundParameters.blurPasses = 8; // 5K - else if (heightModifier >= 2) + else if (resolutionModifier >= 2) backgroundParameters.blurPasses = 5; // 4K - else if (heightModifier >= 1.3) + else if (resolutionModifier >= 1.3) backgroundParameters.blurPasses = 3; // 1440 - else if (heightModifier >= 1) + else if (resolutionModifier >= 1) backgroundParameters.blurPasses = 2; // 1080 // clang-format on @@ -535,9 +535,16 @@ void Window::render() &processedTexture[0]); } - mPostprocessedBackground->initFromPixels( - &processedTexture[0], static_cast(mRenderer->getScreenWidth()), - static_cast(mRenderer->getScreenHeight())); + if (mRenderer->getScreenRotation() == 0 || mRenderer->getScreenRotation() == 180) { + mPostprocessedBackground->initFromPixels( + &processedTexture[0], static_cast(mRenderer->getScreenWidth()), + static_cast(mRenderer->getScreenHeight())); + } + else { + mPostprocessedBackground->initFromPixels( + &processedTexture[0], static_cast(mRenderer->getScreenHeight()), + static_cast(mRenderer->getScreenWidth())); + } mBackgroundOverlay->setImage(mPostprocessedBackground); diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp index 5d92c2b80..3d8404a51 100644 --- a/es-core/src/renderers/Renderer.cpp +++ b/es-core/src/renderers/Renderer.cpp @@ -119,7 +119,30 @@ bool Renderer::createWindow() mScreenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ? Settings::getInstance()->getInt("ScreenOffsetY") : 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 // games or when manually switching windows using the task switcher). @@ -224,6 +247,11 @@ bool Renderer::createWindow() sScreenWidthModifier = static_cast(sScreenWidth) / 1920.0f; sScreenAspectRatio = static_cast(sScreenWidth) / static_cast(sScreenHeight); + if (sIsVerticalOrientation) + sScreenResolutionModifier = sScreenWidth / 1080.0f; + else + sScreenResolutionModifier = sScreenHeight / 1080.0f; + LOG(LogInfo) << "Setting up OpenGL..."; if (!createContext()) @@ -265,18 +293,41 @@ bool Renderer::init() viewport.y = mWindowHeight - mScreenOffsetY - sScreenHeight; viewport.w = sScreenWidth; viewport.h = sScreenHeight; - projection = glm::ortho(0.0f, static_cast(sScreenWidth), - static_cast(sScreenHeight), 0.0f, -1.0f, 1.0f); - projection = glm::rotate(projection, glm::radians(180.0f), {0.0f, 0.0f, 1.0f}); - mProjectionMatrixRotated = - glm::translate(projection, {sScreenWidth * -1.0f, sScreenHeight * -1.0f, 0.0f}); + + projection = glm::ortho(0.0f, static_cast(sScreenHeight), + static_cast(sScreenWidth), 0.0f, -1.0f, 1.0f); + + if (mScreenRotation == 0) { + mProjectionMatrix = glm::ortho(0.0f, static_cast(sScreenWidth), + static_cast(sScreenHeight), 0.0f, -1.0f, 1.0f); + } + else if (mScreenRotation == 90) { + projection = glm::ortho(0.0f, static_cast(sScreenHeight), + static_cast(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(sScreenWidth), + static_cast(sScreenHeight), 0.0f, -1.0f, 1.0f); + projection = glm::rotate(projection, glm::radians(180.0f), {0.0f, 0.0f, 1.0f}); + mProjectionMatrix = + glm::translate(projection, {sScreenWidth * -1.0f, sScreenHeight * -1.0f, 0.0f}); + } + else if (mScreenRotation == 270) { + projection = glm::ortho(0.0f, static_cast(sScreenHeight), + static_cast(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(sScreenWidth), + static_cast(sScreenHeight), 0.0f, -1.0f, 1.0f); viewport.x = mScreenOffsetX; viewport.y = mScreenOffsetY; viewport.w = sScreenWidth; viewport.h = sScreenHeight; - mProjectionMatrix = glm::ortho(0.0f, static_cast(sScreenWidth), - static_cast(sScreenHeight), 0.0f, -1.0f, 1.0f); // This is required to avoid a brief white screen flash during startup on some systems. drawRect(0.0f, 0.0f, static_cast(getScreenWidth()), @@ -301,12 +352,20 @@ void Renderer::pushClipRect(const glm::ivec2& pos, const glm::ivec2& size) if (box.h == 0) box.h = sScreenHeight - box.y; - if (mScreenRotated) { - box = Rect {mWindowWidth - mScreenOffsetX - box.x - box.w, - mWindowHeight - mScreenOffsetY - box.y - box.h, box.w, box.h}; + if (mScreenRotation == 0) { + box = {mScreenOffsetX + box.x, mScreenOffsetY + box.y, box.w, box.h}; } - else { - box = Rect {mScreenOffsetX + box.x, mScreenOffsetY + box.y, box.w, box.h}; + else if (mScreenRotation == 90) { + 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. @@ -328,7 +387,6 @@ void Renderer::pushClipRect(const glm::ivec2& pos, const glm::ivec2& size) box.h = 0; mClipStack.push(box); - setScissor(box); } diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h index 4e51f2cd6..8f14e9b89 100644 --- a/es-core/src/renderers/Renderer.h +++ b/es-core/src/renderers/Renderer.h @@ -52,7 +52,8 @@ public: PREMULTIPLIED = 0x00000001, FONT_TEXTURE = 0x00000002, POST_PROCESSING = 0x00000004, - CLIPPING = 0x00000008 + CLIPPING = 0x00000008, + ROTATED = 0x00000010 // Screen rotated 90 or 270 degrees. }; // clang-format on @@ -155,16 +156,11 @@ public: const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA, const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA); - const glm::mat4& getProjectionMatrix() - { - if (mScreenRotated) - return mProjectionMatrixRotated; - else - return mProjectionMatrix; - } - const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrix; } + const glm::mat4& getProjectionMatrix() { return mProjectionMatrix; } + const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrixNormal; } 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(mWindowWidth); } const float getWindowHeight() { return static_cast(mWindowHeight); } static const float getScreenWidth() { return static_cast(sScreenWidth); } @@ -172,6 +168,7 @@ public: static const float getScreenWidthModifier() { return sScreenWidthModifier; } static const float getScreenHeightModifier() { return sScreenHeightModifier; } static const float getScreenAspectRatio() { return sScreenAspectRatio; } + static const float getScreenResolutionModifier() { return sScreenResolutionModifier; } static constexpr glm::mat4 getIdentity() { return glm::mat4 {1.0f}; } glm::mat4 mTrans {getIdentity()}; @@ -215,19 +212,21 @@ private: std::stack mClipStack; SDL_Window* mSDLWindow {nullptr}; glm::mat4 mProjectionMatrix {}; - glm::mat4 mProjectionMatrixRotated {}; + glm::mat4 mProjectionMatrixNormal {}; int mWindowWidth {0}; int mWindowHeight {0}; static inline int sScreenWidth {0}; static inline int sScreenHeight {0}; int mScreenOffsetX {0}; int mScreenOffsetY {0}; - bool mScreenRotated {0}; - bool mInitialCursorState {1}; + int mScreenRotation {0}; + bool mInitialCursorState {true}; + static inline bool sIsVerticalOrientation {false}; // Screen resolution modifiers relative to the 1920x1080 reference. static inline float sScreenHeightModifier {0.0f}; static inline float sScreenWidthModifier {0.0f}; static inline float sScreenAspectRatio {0.0f}; + static inline float sScreenResolutionModifier {0.0f}; }; #endif // ES_CORE_RENDERER_RENDERER_H diff --git a/es-core/src/renderers/RendererOpenGL.cpp b/es-core/src/renderers/RendererOpenGL.cpp index 6f9136c1d..8355c04fb 100644 --- a/es-core/src/renderers/RendererOpenGL.cpp +++ b/es-core/src/renderers/RendererOpenGL.cpp @@ -269,13 +269,22 @@ bool RendererOpenGL::createContext() uint8_t data[4] {255, 255, 255, 255}; mWhiteTexture = createTexture(TextureType::BGRA, false, false, false, true, 1, 1, data); - mPostProcTexture1 = createTexture(TextureType::BGRA, false, false, false, false, - static_cast(getScreenWidth()), - static_cast(getScreenHeight()), nullptr); + unsigned int textureWidth {0}; + unsigned int textureHeight {0}; - mPostProcTexture2 = createTexture(TextureType::BGRA, false, false, false, false, - static_cast(getScreenWidth()), - static_cast(getScreenHeight()), nullptr); + if (getScreenRotation() == 0 || getScreenRotation() == 180) { + textureWidth = static_cast(getScreenWidth()); + textureHeight = static_cast(getScreenHeight()); + } + else { + textureWidth = static_cast(getScreenHeight()); + textureHeight = static_cast(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. 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_DYNAMIC_DRAW)); mBlurHorizontalShader->setTextureSize({width, height}); + mBlurHorizontalShader->setFlags(vertices->shaderFlags); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); mLastShader = mBlurHorizontalShader; } @@ -511,6 +521,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices, GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, GL_DYNAMIC_DRAW)); mBlurVerticalShader->setTextureSize({width, height}); + mBlurVerticalShader->setFlags(vertices->shaderFlags); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); mLastShader = mBlurVerticalShader; } @@ -521,12 +532,12 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices, mScanlinelShader = getShaderProgram(Shader::SCANLINES); float shaderWidth {width * 1.2f}; // Scale the scanlines relative to screen resolution. - float screenHeightModifier {getScreenHeightModifier()}; + float resolutionModifier {getScreenResolutionModifier()}; float relativeHeight {height / getScreenHeight()}; float shaderHeight {0.0f}; if (relativeHeight == 1.0f) { // Full screen. - float modifier {1.30f - (0.1f * screenHeightModifier)}; + float modifier {1.30f - (0.1f * resolutionModifier)}; shaderHeight = height * modifier; } else { @@ -535,7 +546,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices, // scanlines to videos with non-standard aspect ratios. float relativeWidth {width / getScreenWidth()}; 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; } if (mScanlinelShader) { @@ -550,6 +561,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices, mScanlinelShader->setBrightness(vertices->brightness); mScanlinelShader->setSaturation(vertices->saturation); mScanlinelShader->setTextureSize({shaderWidth, shaderHeight}); + mScanlinelShader->setFlags(vertices->shaderFlags); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); mLastShader = mScanlinelShader; } @@ -566,6 +578,7 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders, float heightf {getScreenHeight()}; GLuint width {static_cast(widthf)}; GLuint height {static_cast(heightf)}; + const int screenRotation {getScreenRotation()}; // Set vertex positions and texture coordinates to full screen as all // post-processing is applied to the complete screen area. @@ -581,6 +594,9 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders, vertices->dimming = parameters.dimming; vertices->shaderFlags = ShaderFlags::POST_PROCESSING | ShaderFlags::PREMULTIPLIED; + if (screenRotation == 90 || screenRotation == 270) + vertices->shaderFlags |= ShaderFlags::ROTATED; + if (shaders & Shader::CORE) shaderList.push_back(Shader::CORE); if (shaders & Shader::BLUR_HORIZONTAL) @@ -595,16 +611,52 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders, GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1)); - // Blit the screen contents to mPostProcTexture. - GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, - GL_NEAREST)); + int shaderCalls {0}; + bool evenBlurPasses {true}; - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO2)); + 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)); 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]; int shaderPasses {1}; // 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; } - for (int p = 0; p < shaderPasses; ++p) { - if (textureRGBA == nullptr && 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. + for (int p {0}; p < shaderPasses; ++p) { + if (!textureRGBA && i == shaderList.size() - 1 && p == shaderPasses - 1) { GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); drawTriangleStrips(vertices, 4, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA); break; } - // Apply/render the shaders. + drawTriangleStrips(vertices, 4, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA); - ++drawCalls; + if (firstFBO) { bindTexture(mPostProcTexture2); 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)); #if defined(USE_OPENGLES) - GL_CHECK_ERROR( - glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, textureRGBA)); + if (screenRotation == 0 || screenRotation == 180) + GL_CHECK_ERROR( + 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 - 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 GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); } - if (getScreenRotated()) - setMatrix(getIdentity()); - GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)); } diff --git a/resources/shaders/glsl/blur_horizontal.glsl b/resources/shaders/glsl/blur_horizontal.glsl index 4363f2378..004b41b5c 100644 --- a/resources/shaders/glsl/blur_horizontal.glsl +++ b/resources/shaders/glsl/blur_horizontal.glsl @@ -30,15 +30,28 @@ void main() precision mediump float; #endif +uniform uint shaderFlags; uniform vec2 textureSize; uniform sampler2D textureSampler; in vec2 texCoord; 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() { + 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); float sampleOffsets[5] = float[5](0.0, 1.4347826, 3.3478260, 5.2608695, 7.1739130); diff --git a/resources/shaders/glsl/blur_vertical.glsl b/resources/shaders/glsl/blur_vertical.glsl index 505c9877e..0d14a2ff4 100644 --- a/resources/shaders/glsl/blur_vertical.glsl +++ b/resources/shaders/glsl/blur_vertical.glsl @@ -30,15 +30,28 @@ void main() precision mediump float; #endif +uniform uint shaderFlags; uniform vec2 textureSize; uniform sampler2D textureSampler; in vec2 texCoord; 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() { + 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); float sampleOffsets[5] = float[5](0.0, 1.4347826, 3.3478260, 5.2608695, 7.1739130); diff --git a/resources/shaders/glsl/core.glsl b/resources/shaders/glsl/core.glsl index 9e60a02d2..cc1f04dcd 100644 --- a/resources/shaders/glsl/core.glsl +++ b/resources/shaders/glsl/core.glsl @@ -54,6 +54,7 @@ out vec4 FragColor; // 0x00000002 - Font texture // 0x00000004 - Post processing // 0x00000008 - Clipping +// 0x00000010 - Screen rotated 90 or 270 degrees void main() { diff --git a/resources/shaders/glsl/scanlines.glsl b/resources/shaders/glsl/scanlines.glsl index 39df227f4..cb68ee18f 100644 --- a/resources/shaders/glsl/scanlines.glsl +++ b/resources/shaders/glsl/scanlines.glsl @@ -60,6 +60,7 @@ uniform vec2 textureSize; uniform float opacity; uniform float brightness; uniform float saturation; +uniform uint shaderFlags; uniform sampler2D textureSampler; in vec2 texCoord; in vec2 onex; @@ -67,8 +68,6 @@ in vec2 oney; in vec4 colorShift; out vec4 FragColor; -#define SourceSize vec4(textureSize, 1.0 / textureSize) - #ifdef PARAMETER_UNIFORM uniform float SPOT_WIDTH; uniform float SPOT_HEIGHT; @@ -95,8 +94,25 @@ uniform float OutputGamma; w = 1.0 - 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() { + 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 pixel_center = floor(coords) + vec2(0.5, 0.5); vec2 texture_coords = pixel_center * SourceSize.zw; @@ -127,7 +143,11 @@ void main() color = color + colorNB * vec4(h_weight_01); // 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; WEIGHT(v_weight_00); color *= vec4(v_weight_00);