diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index 189b1925a..0f0add7a4 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -1163,7 +1163,7 @@ void FileData::launchGame() // flickering and to avoid showing the game launch message briefly when returning // from the game. if (!runInBackground) - Renderer::swapBuffers(); + Renderer::getInstance()->swapBuffers(); Scripting::fireEvent("game-start", romPath, getSourceFileData()->metadata.get("name"), getSourceFileData()->getSystem()->getName(), diff --git a/es-app/src/MediaViewer.cpp b/es-app/src/MediaViewer.cpp index 5cbf9ff05..d7e59d1ef 100644 --- a/es-app/src/MediaViewer.cpp +++ b/es-app/src/MediaViewer.cpp @@ -13,7 +13,8 @@ #include "views/ViewController.h" MediaViewer::MediaViewer() - : mVideo {nullptr} + : mRenderer {Renderer::getInstance()} + , mVideo {nullptr} , mImage {nullptr} { Window::getInstance()->setMediaViewer(this); @@ -77,11 +78,11 @@ void MediaViewer::update(int deltaTime) void MediaViewer::render(const glm::mat4& /*parentTrans*/) { glm::mat4 trans {Renderer::getIdentity()}; - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); // Render a black background below the game media. - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), - 0x000000FF, 0x000000FF); + mRenderer->drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), + 0x000000FF, 0x000000FF); if (mVideo && !mDisplayingImage) { mVideo->render(trans); @@ -112,17 +113,17 @@ void MediaViewer::render(const glm::mat4& /*parentTrans*/) } if (shaders != 0) - Renderer::shaderPostprocessing(shaders, videoParameters); + mRenderer->shaderPostprocessing(shaders, videoParameters); } else if (mImage && mImage->hasImage() && mImage->getSize() != glm::vec2 {}) { mImage->render(trans); if (mCurrentImageIndex == mScreenshotIndex && Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) - Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); + mRenderer->shaderPostprocessing(Renderer::SHADER_SCANLINES); else if (mCurrentImageIndex == mTitleScreenIndex && Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) - Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); + mRenderer->shaderPostprocessing(Renderer::SHADER_SCANLINES); // This is necessary so that the video loops if viewing an image when // the video ends. diff --git a/es-app/src/MediaViewer.h b/es-app/src/MediaViewer.h index 371c9445d..981c2e72d 100644 --- a/es-app/src/MediaViewer.h +++ b/es-app/src/MediaViewer.h @@ -36,6 +36,7 @@ private: void showNext() override; void showPrevious() override; + Renderer* mRenderer; FileData* mGame; bool mHasVideo; diff --git a/es-app/src/Screensaver.cpp b/es-app/src/Screensaver.cpp index 65798d87c..e89799e8e 100644 --- a/es-app/src/Screensaver.cpp +++ b/es-app/src/Screensaver.cpp @@ -30,7 +30,8 @@ #define FADE_TIME 300.0f Screensaver::Screensaver() - : mWindow {Window::getInstance()} + : mRenderer {Renderer::getInstance()} + , mWindow {Window::getInstance()} , mState {STATE_INACTIVE} , mImageScreensaver {nullptr} , mVideoScreensaver {nullptr} @@ -238,12 +239,12 @@ void Screensaver::renderScreensaver() { std::string screensaverType = Settings::getInstance()->getString("ScreensaverType"); glm::mat4 trans {Renderer::getIdentity()}; - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (mVideoScreensaver && screensaverType == "video") { // Render a black background below the video. - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), - 0x000000FF, 0x000000FF); + mRenderer->drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), + 0x000000FF, 0x000000FF); // Only render the video if the state requires it. if (static_cast(mState) >= STATE_FADE_IN_VIDEO) @@ -251,8 +252,8 @@ void Screensaver::renderScreensaver() } else if (mImageScreensaver && screensaverType == "slideshow") { // Render a black background below the image. - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), - 0x000000FF, 0x000000FF); + mRenderer->drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), + 0x000000FF, 0x000000FF); // Only render the image if the state requires it. if (static_cast(mState) >= STATE_FADE_IN_VIDEO) { @@ -268,11 +269,11 @@ void Screensaver::renderScreensaver() if (Settings::getInstance()->getString("ScreensaverType") == "slideshow") { if (mHasMediaFiles) { if (Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) - Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); + mRenderer->shaderPostprocessing(Renderer::SHADER_SCANLINES); if (Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo") && mGameOverlay) { if (mGameOverlayRectangleCoords.size() == 4) { - Renderer::drawRect( + mRenderer->drawRect( mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3], 0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn); @@ -319,11 +320,11 @@ void Screensaver::renderScreensaver() } if (shaders != 0) - Renderer::shaderPostprocessing(shaders, videoParameters); + mRenderer->shaderPostprocessing(shaders, videoParameters); if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo") && mGameOverlay) { if (mGameOverlayRectangleCoords.size() == 4) { - Renderer::drawRect( + mRenderer->drawRect( mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3], 0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn); @@ -347,7 +348,7 @@ void Screensaver::renderScreensaver() Renderer::postProcessingParams dimParameters; dimParameters.dimming = mDimValue; dimParameters.saturation = mSaturationAmount; - Renderer::shaderPostprocessing(Renderer::SHADER_CORE, dimParameters); + mRenderer->shaderPostprocessing(Renderer::SHADER_CORE, dimParameters); if (mDimValue > 0.63) mDimValue = glm::clamp(mDimValue - 0.015f, 0.68f, 1.0f); if (mSaturationAmount > 0.0) @@ -356,7 +357,7 @@ void Screensaver::renderScreensaver() else if (Settings::getInstance()->getString("ScreensaverType") == "black") { Renderer::postProcessingParams blackParameters; blackParameters.dimming = mDimValue; - Renderer::shaderPostprocessing(Renderer::SHADER_CORE, blackParameters); + mRenderer->shaderPostprocessing(Renderer::SHADER_CORE, blackParameters); if (mDimValue > 0.0) mDimValue = glm::clamp(mDimValue - 0.045f, 0.0f, 1.0f); } @@ -567,8 +568,8 @@ void Screensaver::generateOverlayInfo() if (mGameName == "" || mSystemName == "") return; - float posX {Renderer::getWindowWidth() * 0.023f}; - float posY {Renderer::getWindowHeight() * 0.02f}; + float posX {mRenderer->getWindowWidth() * 0.023f}; + float posY {mRenderer->getWindowHeight() * 0.02f}; std::string favoriteChar; if (mCurrentGame && mCurrentGame->getFavorite()) @@ -595,7 +596,7 @@ void Screensaver::generateOverlayInfo() else textSizeX = mGameOverlayFont[0].get()->sizeText(systemName).x; - float marginX {Renderer::getWindowWidth() * 0.01f}; + float marginX {mRenderer->getWindowWidth() * 0.01f}; mGameOverlayRectangleCoords.clear(); mGameOverlayRectangleCoords.push_back(posX - marginX); diff --git a/es-app/src/Screensaver.h b/es-app/src/Screensaver.h index 20ad08569..609e6d7fb 100644 --- a/es-app/src/Screensaver.h +++ b/es-app/src/Screensaver.h @@ -58,6 +58,7 @@ private: STATE_SCREENSAVER_ACTIVE }; + Renderer* mRenderer; Window* mWindow; STATE mState; diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index 06469887a..a0ed0dffc 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -24,6 +24,8 @@ #include "scrapers/Scraper.h" #include "views/ViewController.h" +#include + GuiGamelistOptions::GuiGamelistOptions(SystemData* system) : mMenu {"OPTIONS"} , mSystem {system} diff --git a/es-app/src/guis/GuiLaunchScreen.cpp b/es-app/src/guis/GuiLaunchScreen.cpp index 2ea9aa3c5..8703a637c 100644 --- a/es-app/src/guis/GuiLaunchScreen.cpp +++ b/es-app/src/guis/GuiLaunchScreen.cpp @@ -15,7 +15,8 @@ #include "utils/StringUtil.h" GuiLaunchScreen::GuiLaunchScreen() - : mBackground {":/graphics/frame.svg"} + : mRenderer {Renderer::getInstance()} + , mBackground {":/graphics/frame.svg"} , mGrid {nullptr} , mMarquee {nullptr} { @@ -224,7 +225,7 @@ void GuiLaunchScreen::render(const glm::mat4& /*parentTrans*/) setScale(mScaleUp); glm::mat4 trans {Renderer::getIdentity() * getTransform()}; - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); GuiComponent::renderChildren(trans); diff --git a/es-app/src/guis/GuiLaunchScreen.h b/es-app/src/guis/GuiLaunchScreen.h index abda4f32a..af24f4329 100644 --- a/es-app/src/guis/GuiLaunchScreen.h +++ b/es-app/src/guis/GuiLaunchScreen.h @@ -33,6 +33,7 @@ public: void render(const glm::mat4& parentTrans) override; private: + Renderer* mRenderer; NinePatchComponent mBackground; ComponentGrid* mGrid; diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index 4b34ae21c..835db17c7 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -38,7 +38,8 @@ #define FAILED_VERIFICATION_RETRIES 8 GuiScraperSearch::GuiScraperSearch(SearchType type, unsigned int scrapeCount) - : mGrid {glm::ivec2 {5, 3}} + : mRenderer {Renderer::getInstance()} + , mGrid {glm::ivec2 {5, 3}} , mSearchType {type} , mScrapeCount {scrapeCount} , mRefinedSearch {false} @@ -630,10 +631,10 @@ void GuiScraperSearch::render(const glm::mat4& parentTrans) glm::mat4 trans {parentTrans * getTransform()}; renderChildren(trans); - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x00000009, 0x00000009); + mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x00000009, 0x00000009); if (mBlockAccept) { - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); mBusyAnim.render(trans); } } diff --git a/es-app/src/guis/GuiScraperSearch.h b/es-app/src/guis/GuiScraperSearch.h index 8184ea37a..2901e6ab9 100644 --- a/es-app/src/guis/GuiScraperSearch.h +++ b/es-app/src/guis/GuiScraperSearch.h @@ -119,6 +119,7 @@ private: // Resolve any metadata assets that need to be downloaded and return. void returnResult(ScraperSearchResult result); + Renderer* mRenderer; ComponentGrid mGrid; std::shared_ptr mResultName; diff --git a/es-app/src/guis/GuiSettings.cpp b/es-app/src/guis/GuiSettings.cpp index 559c904f1..f99ef569c 100644 --- a/es-app/src/guis/GuiSettings.cpp +++ b/es-app/src/guis/GuiSettings.cpp @@ -21,6 +21,8 @@ #include "views/GamelistView.h" #include "views/ViewController.h" +#include + GuiSettings::GuiSettings(std::string title) : mMenu {title} , mGoToSystem {nullptr} diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index f954e8304..a4b91581d 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -57,6 +57,7 @@ namespace { SDL_Event event {}; + Renderer* renderer {nullptr}; Window* window {nullptr}; int lastTime {0}; @@ -474,7 +475,7 @@ void applicationLoop() window->update(deltaTime); window->render(); - Renderer::swapBuffers(); + renderer->swapBuffers(); Log::flush(); #if !defined(__EMSCRIPTEN__) } @@ -601,6 +602,7 @@ int main(int argc, char* argv[]) } } + renderer = Renderer::getInstance(); window = Window::getInstance(); ViewController::getInstance(); CollectionSystemsManager::getInstance(); diff --git a/es-app/src/views/GamelistView.cpp b/es-app/src/views/GamelistView.cpp index b0a4b0990..b52ea244b 100644 --- a/es-app/src/views/GamelistView.cpp +++ b/es-app/src/views/GamelistView.cpp @@ -18,8 +18,9 @@ GamelistView::GamelistView(FileData* root) : GamelistBase {root} - , mLegacyMode {false} + , mRenderer {Renderer::getInstance()} , mViewStyle {ViewController::BASIC} + , mLegacyMode {false} { mViewStyle = ViewController::getInstance()->getState().viewstyle; @@ -247,9 +248,9 @@ void GamelistView::render(const glm::mat4& parentTrans) glm::ivec2 size {static_cast(std::round(mSize.x * scaleX)), static_cast(std::round(mSize.y * scaleY))}; - Renderer::pushClipRect(pos, size); + mRenderer->pushClipRect(pos, size); renderChildren(trans); - Renderer::popClipRect(); + mRenderer->popClipRect(); } HelpStyle GamelistView::getHelpStyle() diff --git a/es-app/src/views/GamelistView.h b/es-app/src/views/GamelistView.h index 4bc6ec5a6..5b35652a5 100644 --- a/es-app/src/views/GamelistView.h +++ b/es-app/src/views/GamelistView.h @@ -102,8 +102,9 @@ private: void legacyInitMDLabels(); void legacyInitMDValues(); - bool mLegacyMode; + Renderer* mRenderer; ViewController::GamelistViewStyle mViewStyle; + bool mLegacyMode; std::shared_ptr mTheme; std::vector mThemeExtras; diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 3ceeac473..c5b1f3aef 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -22,7 +22,8 @@ #endif SystemView::SystemView() - : mCamOffset {0.0f} + : mRenderer {Renderer::getInstance()} + , mCamOffset {0.0f} , mFadeOpacity {0.0f} , mPreviousScrollVelocity {0} , mUpdatedGameCount {false} @@ -1048,7 +1049,7 @@ void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary) elementTrans = glm::translate(elementTrans, glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}); - Renderer::pushClipRect( + mRenderer->pushClipRect( glm::ivec2 {static_cast(glm::round(elementTrans[3].x)), static_cast(glm::round(elementTrans[3].y))}, glm::ivec2 {static_cast(mSize.x), static_cast(mSize.y)}); @@ -1081,7 +1082,7 @@ void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary) mLegacySystemInfo->render(elementTrans); } - Renderer::popClipRect(); + mRenderer->popClipRect(); } } } diff --git a/es-app/src/views/SystemView.h b/es-app/src/views/SystemView.h index faa2c722b..203a623df 100644 --- a/es-app/src/views/SystemView.h +++ b/es-app/src/views/SystemView.h @@ -108,6 +108,7 @@ private: void legacyApplyTheme(const std::shared_ptr& theme); void renderElements(const glm::mat4& parentTrans, bool abovePrimary); + Renderer* mRenderer; std::unique_ptr mCarousel; std::unique_ptr mLegacySystemInfo; std::vector mSystemElements; diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index e6517b866..0e562bf70 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -31,7 +31,8 @@ #include "views/SystemView.h" ViewController::ViewController() noexcept - : mNoGamesMessageBox {nullptr} + : mRenderer {Renderer::getInstance()} + , mNoGamesMessageBox {nullptr} , mCurrentView {nullptr} , mPreviousView {nullptr} , mSkipView {nullptr} @@ -201,7 +202,7 @@ void ViewController::invalidAlternativeEmulatorDialog() void ViewController::goToStart(bool playTransition) { // Needed to avoid segfaults during emergency shutdown. - if (Renderer::getSDLWindow() == nullptr) + if (mRenderer->getSDLWindow() == nullptr) return; #if defined(__APPLE__) @@ -287,7 +288,7 @@ void ViewController::cancelViewTransitions() void ViewController::stopScrolling() { - if (Renderer::getSDLWindow() == nullptr) + if (mRenderer->getSDLWindow() == nullptr) return; mSystemListView->stopScrolling(); @@ -905,9 +906,9 @@ void ViewController::render(const glm::mat4& parentTrans) // Fade out. if (mFadeOpacity) { unsigned int fadeColor = 0x00000000 | static_cast(mFadeOpacity * 255); - Renderer::setMatrix(parentTrans); - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), - fadeColor, fadeColor); + mRenderer->setMatrix(parentTrans); + mRenderer->drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), + fadeColor, fadeColor); } } @@ -999,7 +1000,7 @@ void ViewController::reloadGamelistView(GamelistView* view, bool reloadTheme) void ViewController::reloadAll() { - if (Renderer::getSDLWindow() == nullptr) + if (mRenderer->getSDLWindow() == nullptr) return; cancelViewTransitions(); diff --git a/es-app/src/views/ViewController.h b/es-app/src/views/ViewController.h index eb589ab80..88cdaab07 100644 --- a/es-app/src/views/ViewController.h +++ b/es-app/src/views/ViewController.h @@ -151,6 +151,7 @@ private: void launch(FileData* game); + Renderer* mRenderer; std::string mNoGamesErrorMessage; std::string mRomDirectory; GuiMsgBox* mNoGamesMessageBox; diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index b79d626c6..45157f1f6 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -74,6 +74,7 @@ set(CORE_HEADERS # Renderers ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer_GL21.h ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Shader_GL21.h # Resources diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index faf5482da..9b6e3b502 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -23,7 +23,8 @@ #define CLOCK_BACKGROUND_CREATION false Window::Window() noexcept - : mScreensaver {nullptr} + : mRenderer {Renderer::getInstance()} + , mScreensaver {nullptr} , mMediaViewer {nullptr} , mLaunchScreen {nullptr} , mInfoPopup {nullptr} @@ -102,7 +103,7 @@ GuiComponent* Window::peekGui() bool Window::init() { - if (!Renderer::init()) { + if (!mRenderer->init()) { LOG(LogError) << "Renderer failed to initialize."; return false; } @@ -146,7 +147,7 @@ void Window::deinit() InputManager::getInstance().deinit(); ResourceManager::getInstance().unloadAll(); - Renderer::deinit(); + mRenderer->deinit(); } void Window::input(InputConfig* config, Input input) @@ -476,16 +477,16 @@ void Window::render() // Also dim the background slightly. backgroundParameters.dimming = 0.60f; - Renderer::shaderPostprocessing(Renderer::SHADER_CORE | - Renderer::SHADER_BLUR_HORIZONTAL | - Renderer::SHADER_BLUR_VERTICAL, - backgroundParameters, &processedTexture[0]); + mRenderer->shaderPostprocessing(Renderer::SHADER_CORE | + Renderer::SHADER_BLUR_HORIZONTAL | + Renderer::SHADER_BLUR_VERTICAL, + backgroundParameters, &processedTexture[0]); } else { // Dim the background slightly. backgroundParameters.dimming = 0.60f; - Renderer::shaderPostprocessing(Renderer::SHADER_CORE, backgroundParameters, - &processedTexture[0]); + mRenderer->shaderPostprocessing(Renderer::SHADER_CORE, backgroundParameters, + &processedTexture[0]); } mPostprocessedBackground->initFromPixels( @@ -546,10 +547,10 @@ void Window::render() // Render the quick list scrolling overlay, which is triggered in IList. if (mListScrollOpacity != 0.0f) { - Renderer::setMatrix(Renderer::getIdentity()); - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), - 0x00000000 | static_cast(mListScrollOpacity * 255.0f), - 0x00000000 | static_cast(mListScrollOpacity * 255.0f)); + mRenderer->setMatrix(Renderer::getIdentity()); + mRenderer->drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), + 0x00000000 | static_cast(mListScrollOpacity * 255.0f), + 0x00000000 | static_cast(mListScrollOpacity * 255.0f)); glm::vec2 offset {mListScrollFont->sizeText(mListScrollText)}; offset.x = (Renderer::getScreenWidth() - offset.x) * 0.5f; @@ -604,7 +605,7 @@ void Window::render() mLaunchScreen->render(trans); if (Settings::getInstance()->getBool("DisplayGPUStatistics") && mFrameDataText) { - Renderer::setMatrix(Renderer::getIdentity()); + mRenderer->setMatrix(Renderer::getIdentity()); mDefaultFonts.at(1)->renderTextCache(mFrameDataText.get()); } } @@ -612,9 +613,9 @@ void Window::render() void Window::renderLoadingScreen(std::string text) { glm::mat4 trans {Renderer::getIdentity()}; - Renderer::setMatrix(trans); - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), - 0x000000FF, 0x000000FF); + mRenderer->setMatrix(trans); + mRenderer->drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), + 0x000000FF, 0x000000FF); ImageComponent splash(true); splash.setImage(":/graphics/splash.svg"); @@ -629,11 +630,11 @@ void Window::renderLoadingScreen(std::string text) float x {std::round((Renderer::getScreenWidth() - cache->metrics.size.x) / 2.0f)}; float y {std::round(Renderer::getScreenHeight() * 0.835f)}; trans = glm::translate(trans, glm::vec3 {x, y, 0.0f}); - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); font->renderTextCache(cache); delete cache; - Renderer::swapBuffers(); + mRenderer->swapBuffers(); } void Window::renderListScrollOverlay(const float opacity, const std::string& text) diff --git a/es-core/src/Window.h b/es-core/src/Window.h index beacd950f..b81fdc29a 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -159,6 +159,7 @@ private: // Returns true if at least one component on the stack is processing. bool isProcessing(); + Renderer* mRenderer; HelpComponent* mHelp; ImageComponent* mBackgroundOverlay; float mBackgroundOverlayOpacity; diff --git a/es-core/src/components/ButtonComponent.cpp b/es-core/src/components/ButtonComponent.cpp index da878ae2c..00147f2f3 100644 --- a/es-core/src/components/ButtonComponent.cpp +++ b/es-core/src/components/ButtonComponent.cpp @@ -17,7 +17,8 @@ ButtonComponent::ButtonComponent(const std::string& text, const std::function& func, bool upperCase, bool flatStyle) - : mBox {":/graphics/button.svg"} + : mRenderer {Renderer::getInstance()} + , mBox {":/graphics/button.svg"} , mFont {Font::get(FONT_SIZE_MEDIUM)} , mPadding {0.0f, 0.0f, 0.0f, 0.0f} , mFocused {false} @@ -112,16 +113,16 @@ void ButtonComponent::render(const glm::mat4& parentTrans) if (mFlatStyle) { if (mFocused) { - Renderer::setMatrix(trans); - Renderer::drawRect(mPadding.x, mPadding.y, mSize.x - mPadding.x - mPadding.z, - mSize.y - mPadding.y - mPadding.w, mFlatColorFocused, - mFlatColorFocused); + mRenderer->setMatrix(trans); + mRenderer->drawRect(mPadding.x, mPadding.y, mSize.x - mPadding.x - mPadding.z, + mSize.y - mPadding.y - mPadding.w, mFlatColorFocused, + mFlatColorFocused); } else { - Renderer::setMatrix(trans); - Renderer::drawRect(mPadding.x, mPadding.y, mSize.x - mPadding.x - mPadding.z, - mSize.y - mPadding.y - mPadding.w, mFlatColorUnfocused, - mFlatColorUnfocused); + mRenderer->setMatrix(trans); + mRenderer->drawRect(mPadding.x, mPadding.y, mSize.x - mPadding.x - mPadding.z, + mSize.y - mPadding.y - mPadding.w, mFlatColorUnfocused, + mFlatColorUnfocused); } } else { @@ -134,13 +135,13 @@ void ButtonComponent::render(const glm::mat4& parentTrans) trans = glm::translate(trans, centerOffset); if (Settings::getInstance()->getBool("DebugText")) { - Renderer::drawRect(centerOffset.x, 0.0f, mTextCache->metrics.size.x, mSize.y, - 0x00000033, 0x00000033); - Renderer::drawRect(mBox.getPosition().x, 0.0f, mBox.getSize().x, mSize.y, 0x0000FF33, - 0x0000FF33); + mRenderer->drawRect(centerOffset.x, 0.0f, mTextCache->metrics.size.x, mSize.y, + 0x00000033, 0x00000033); + mRenderer->drawRect(mBox.getPosition().x, 0.0f, mBox.getSize().x, mSize.y, 0x0000FF33, + 0x0000FF33); } - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); mTextCache->setColor(getCurTextColor()); mFont->renderTextCache(mTextCache.get()); diff --git a/es-core/src/components/ButtonComponent.h b/es-core/src/components/ButtonComponent.h index 57add5f38..e15a5ad4d 100644 --- a/es-core/src/components/ButtonComponent.h +++ b/es-core/src/components/ButtonComponent.h @@ -50,6 +50,7 @@ private: unsigned int getCurTextColor() const; void updateImage(); + Renderer* mRenderer; NinePatchComponent mBox; std::shared_ptr mFont; diff --git a/es-core/src/components/CarouselComponent.cpp b/es-core/src/components/CarouselComponent.cpp index b4b6d9e03..3a9b9c949 100644 --- a/es-core/src/components/CarouselComponent.cpp +++ b/es-core/src/components/CarouselComponent.cpp @@ -21,6 +21,7 @@ namespace CarouselComponent::CarouselComponent() : IList {LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP} + , mRenderer {Renderer::getInstance()} , mCamOffset {0.0f} , mPreviousScrollVelocity {0} , mType {HORIZONTAL} @@ -194,11 +195,11 @@ void CarouselComponent::render(const glm::mat4& parentTrans) carouselTrans, glm::vec3 {mOrigin.x * mSize.x * -1.0f, mOrigin.y * mSize.y * -1.0f, 0.0f}); glm::vec2 clipPos {carouselTrans[3].x, carouselTrans[3].y}; - Renderer::setMatrix(carouselTrans); + mRenderer->setMatrix(carouselTrans); // Background box behind logos. - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, mCarouselColor, mCarouselColorEnd, - mColorGradientHorizontal); + mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, mCarouselColor, mCarouselColorEnd, + mColorGradientHorizontal); // Draw logos. // logoSpacing will also include the size of the logo itself. diff --git a/es-core/src/components/CarouselComponent.h b/es-core/src/components/CarouselComponent.h index ec5708b62..a2f412b22 100644 --- a/es-core/src/components/CarouselComponent.h +++ b/es-core/src/components/CarouselComponent.h @@ -68,6 +68,7 @@ protected: } private: + Renderer* mRenderer; std::function mCursorChangedCallback; std::function mCancelTransitionsCallback; diff --git a/es-core/src/components/ComponentGrid.cpp b/es-core/src/components/ComponentGrid.cpp index 3e6f45b7b..bef61668f 100644 --- a/es-core/src/components/ComponentGrid.cpp +++ b/es-core/src/components/ComponentGrid.cpp @@ -13,7 +13,8 @@ using namespace GridFlags; ComponentGrid::ComponentGrid(const glm::ivec2& gridDimensions) - : mGridSize {gridDimensions} + : mRenderer {Renderer::getInstance()} + , mGridSize {gridDimensions} , mCursor {0, 0} { assert(gridDimensions.x > 0 && gridDimensions.y > 0); @@ -429,9 +430,9 @@ void ComponentGrid::render(const glm::mat4& parentTrans) // Draw cell separators. for (size_t i = 0; i < mSeparators.size(); ++i) { - Renderer::setMatrix(trans); - Renderer::drawRect(mSeparators[i][0], mSeparators[i][1], mSeparators[i][2], - mSeparators[i][3], 0xC6C7C6FF, 0xC6C7C6FF); + mRenderer->setMatrix(trans); + mRenderer->drawRect(mSeparators[i][0], mSeparators[i][1], mSeparators[i][2], + mSeparators[i][3], 0xC6C7C6FF, 0xC6C7C6FF); } } diff --git a/es-core/src/components/ComponentGrid.h b/es-core/src/components/ComponentGrid.h index aeb6c8584..7e5999202 100644 --- a/es-core/src/components/ComponentGrid.h +++ b/es-core/src/components/ComponentGrid.h @@ -128,6 +128,7 @@ private: const GridEntry* getCellAt(int x, int y) const; const GridEntry* getCellAt(const glm::ivec2& pos) const { return getCellAt(pos.x, pos.y); } + Renderer* mRenderer; std::vector> mSeparators; glm::ivec2 mGridSize; std::vector mCells; diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 86a4839a9..56d3c4634 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -14,6 +14,7 @@ ComponentList::ComponentList() : IList {LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP} + , mRenderer {Renderer::getInstance()} , mFocused {false} , mSetupCompleted {false} , mBottomCameraOffset {false} @@ -294,8 +295,8 @@ void ComponentList::render(const glm::mat4& parentTrans) const int clipRectSizeX {static_cast(std::round(dim.x))}; const int clipRectSizeY {static_cast(std::round(dim.y))}; - Renderer::pushClipRect(glm::ivec2 {clipRectPosX, clipRectPosY}, - glm::ivec2 {clipRectSizeX, clipRectSizeY}); + mRenderer->pushClipRect(glm::ivec2 {clipRectPosX, clipRectPosY}, + glm::ivec2 {clipRectSizeX, clipRectSizeY}); // Scroll the camera. trans = glm::translate(trans, glm::vec3 {0.0f, -mCameraOffset, 0.0f}); @@ -374,20 +375,21 @@ void ComponentList::render(const glm::mat4& parentTrans) } // Custom rendering. - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); // Draw selector bar. if (mFocused) { const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data); if (mOpacity == 1.0f) { - Renderer::drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight, - 0xFFFFFFFF, 0xFFFFFFFF, false, mOpacity, mDimming, - Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO); + mRenderer->drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight, + 0xFFFFFFFF, 0xFFFFFFFF, false, mOpacity, mDimming, + Renderer::BlendFactor::ONE_MINUS_DST_COLOR, + Renderer::BlendFactor::ZERO); - Renderer::drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight, - 0x777777FF, 0x777777FF, false, mOpacity, mDimming, - Renderer::Blend::ONE, Renderer::Blend::ONE); + mRenderer->drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight, + 0x777777FF, 0x777777FF, false, mOpacity, mDimming, + Renderer::BlendFactor::ONE, Renderer::BlendFactor::ONE); } for (auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); ++it) @@ -395,20 +397,20 @@ void ComponentList::render(const glm::mat4& parentTrans) // Reset matrix if one of these components changed it. if (drawAfterCursor.size()) - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); } // Draw separators. float y = 0; for (unsigned int i = 0; i < mEntries.size(); ++i) { - Renderer::drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(), - 0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, mDimming); + mRenderer->drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(), + 0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, mDimming); y += getRowHeight(mEntries.at(i).data); } - Renderer::drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(), - 0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, mDimming); - Renderer::popClipRect(); + mRenderer->drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(), + 0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, mDimming); + mRenderer->popClipRect(); } float ComponentList::getRowHeight(const ComponentListRow& row) const diff --git a/es-core/src/components/ComponentList.h b/es-core/src/components/ComponentList.h index ac71e025c..a8827690f 100644 --- a/es-core/src/components/ComponentList.h +++ b/es-core/src/components/ComponentList.h @@ -124,6 +124,7 @@ protected: void onCursorChanged(const CursorState& state) override; private: + Renderer* mRenderer; bool mFocused; bool mSetupCompleted; bool mBottomCameraOffset; diff --git a/es-core/src/components/DateTimeEditComponent.cpp b/es-core/src/components/DateTimeEditComponent.cpp index 4547c1459..f5fb3afb7 100644 --- a/es-core/src/components/DateTimeEditComponent.cpp +++ b/es-core/src/components/DateTimeEditComponent.cpp @@ -16,7 +16,8 @@ #include "utils/StringUtil.h" DateTimeEditComponent::DateTimeEditComponent(bool alignRight, DisplayMode dispMode) - : mEditing {false} + : mRenderer {Renderer::getInstance()} + , mEditing {false} , mEditIndex {0} , mDisplayMode {dispMode} , mKeyRepeatDir {0} @@ -181,16 +182,16 @@ void DateTimeEditComponent::render(const glm::mat4& parentTrans) off.x += referenceSize - mTextCache->metrics.size.x; trans = glm::translate(trans, off); - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (Settings::getInstance()->getBool("DebugText")) { - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (mTextCache->metrics.size.x > 0.0f) { - Renderer::drawRect(0.0f, 0.0f - off.y, mSize.x - off.x, mSize.y, 0x0000FF33, - 0x0000FF33); + mRenderer->drawRect(0.0f, 0.0f - off.y, mSize.x - off.x, mSize.y, 0x0000FF33, + 0x0000FF33); } - Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x, mTextCache->metrics.size.y, - 0x00000033, 0x00000033); + mRenderer->drawRect(0.0f, 0.0f, mTextCache->metrics.size.x, mTextCache->metrics.size.y, + 0x00000033, 0x00000033); } mTextCache->setColor((mColor & 0xFFFFFF00) | static_cast(getOpacity() * 255.0f)); @@ -198,9 +199,9 @@ void DateTimeEditComponent::render(const glm::mat4& parentTrans) if (mEditing && mTime != 0) { if (mEditIndex >= 0 && static_cast(mEditIndex) < mCursorBoxes.size()) - Renderer::drawRect(mCursorBoxes[mEditIndex][0], mCursorBoxes[mEditIndex][1], - mCursorBoxes[mEditIndex][2], mCursorBoxes[mEditIndex][3], - 0x00000022, 0x00000022); + mRenderer->drawRect(mCursorBoxes[mEditIndex][0], mCursorBoxes[mEditIndex][1], + mCursorBoxes[mEditIndex][2], mCursorBoxes[mEditIndex][3], + 0x00000022, 0x00000022); } } } diff --git a/es-core/src/components/DateTimeEditComponent.h b/es-core/src/components/DateTimeEditComponent.h index 356ada80f..12b4329d4 100644 --- a/es-core/src/components/DateTimeEditComponent.h +++ b/es-core/src/components/DateTimeEditComponent.h @@ -10,6 +10,7 @@ #define ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H #include "GuiComponent.h" +#include "renderers/Renderer.h" #include "utils/TimeUtil.h" class TextCache; @@ -66,6 +67,7 @@ private: void changeDate(); void updateTextCache(); + Renderer* mRenderer; Utils::Time::DateTime mTime; Utils::Time::DateTime mTimeBeforeEdit; diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index aa2567f27..dbd89cbb3 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -20,7 +20,8 @@ #include "ThemeData.h" FlexboxComponent::FlexboxComponent(std::vector& items) - : mItems {items} + : mRenderer {Renderer::getInstance()} + , mItems {items} , mDirection {DEFAULT_DIRECTION} , mAlignment {DEFAULT_ALIGNMENT} , mLines {DEFAULT_LINES} @@ -42,10 +43,10 @@ void FlexboxComponent::render(const glm::mat4& parentTrans) computeLayout(); glm::mat4 trans {parentTrans * getTransform()}; - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (Settings::getInstance()->getBool("DebugImage")) - Renderer::drawRect(0.0f, 0.0f, ceilf(mSize.x), ceilf(mSize.y), 0xFF000033, 0xFF000033); + mRenderer->drawRect(0.0f, 0.0f, ceilf(mSize.x), ceilf(mSize.y), 0xFF000033, 0xFF000033); for (auto& item : mItems) { if (!item.visible) diff --git a/es-core/src/components/FlexboxComponent.h b/es-core/src/components/FlexboxComponent.h index 8e1bbad98..5081b4098 100644 --- a/es-core/src/components/FlexboxComponent.h +++ b/es-core/src/components/FlexboxComponent.h @@ -81,6 +81,7 @@ private: // Calculate flexbox layout. void computeLayout(); + Renderer* mRenderer; std::vector& mItems; // Layout options. diff --git a/es-core/src/components/GIFAnimComponent.cpp b/es-core/src/components/GIFAnimComponent.cpp index 42fcfa85d..ce0c15d87 100644 --- a/es-core/src/components/GIFAnimComponent.cpp +++ b/es-core/src/components/GIFAnimComponent.cpp @@ -20,7 +20,8 @@ #include "utils/StringUtil.h" GIFAnimComponent::GIFAnimComponent() - : mFrameSize {0} + : mRenderer {Renderer::getInstance()} + , mFrameSize {0} , mAnimFile {nullptr} , mAnimation {nullptr} , mFrame {nullptr} @@ -466,10 +467,10 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans) } } - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (Settings::getInstance()->getBool("DebugImage")) - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); + mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); if (mTexture->getSize().x != 0.0f) { mTexture->bind(); @@ -493,6 +494,6 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans) vertices->convertBGRAToRGBA = true; // Render it. - Renderer::drawTriangleStrips(&vertices[0], 4); + mRenderer->drawTriangleStrips(&vertices[0], 4); } } diff --git a/es-core/src/components/GIFAnimComponent.h b/es-core/src/components/GIFAnimComponent.h index 15e6abba9..27ee14b8a 100644 --- a/es-core/src/components/GIFAnimComponent.h +++ b/es-core/src/components/GIFAnimComponent.h @@ -69,6 +69,7 @@ private: return ftell(reinterpret_cast(handle)); } + Renderer* mRenderer; std::shared_ptr mTexture; std::vector mPictureRGBA; size_t mFrameSize; diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index bd9efd8f0..0ffab5f13 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -30,7 +30,8 @@ glm::vec2 ImageComponent::getSize() const } ImageComponent::ImageComponent(bool forceLoad, bool dynamic) - : mTargetSize {0, 0} + : mRenderer {Renderer::getInstance()} + , mTargetSize {0, 0} , mFlipX {false} , mFlipY {false} , mTargetIsMax {false} @@ -396,14 +397,14 @@ void ImageComponent::render(const glm::mat4& parentTrans) return; glm::mat4 trans {parentTrans * getTransform()}; - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (mTexture && mOpacity > 0.0f) { if (Settings::getInstance()->getBool("DebugImage")) { glm::vec2 targetSizePos {(mTargetSize - mSize) * mOrigin * glm::vec2 {-1.0f}}; - Renderer::drawRect(targetSizePos.x, targetSizePos.y, mTargetSize.x, mTargetSize.y, - 0xFF000033, 0xFF000033); - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); + mRenderer->drawRect(targetSizePos.x, targetSizePos.y, mTargetSize.x, mTargetSize.y, + 0xFF000033, 0xFF000033); + mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); } // An image with zero size would normally indicate a corrupt image file. if (mTexture->getSize() != glm::ivec2 {}) { @@ -422,7 +423,7 @@ void ImageComponent::render(const glm::mat4& parentTrans) mVertices->opacity = mThemeOpacity; mVertices->dimming = mDimming; - Renderer::drawTriangleStrips(&mVertices[0], 4); + mRenderer->drawTriangleStrips(&mVertices[0], 4); } else { if (!mTexture) { diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 4c6873e61..8eda39e58 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -102,6 +102,7 @@ public: std::vector getHelpPrompts() override; private: + Renderer* mRenderer; glm::vec2 mTargetSize; bool mFlipX; diff --git a/es-core/src/components/LottieAnimComponent.cpp b/es-core/src/components/LottieAnimComponent.cpp index 506d7639f..c90aeb507 100644 --- a/es-core/src/components/LottieAnimComponent.cpp +++ b/es-core/src/components/LottieAnimComponent.cpp @@ -16,7 +16,8 @@ #include "resources/ResourceManager.h" LottieAnimComponent::LottieAnimComponent() - : mCacheFrames {true} + : mRenderer {Renderer::getInstance()} + , mCacheFrames {true} , mMaxCacheSize {0} , mCacheSize {0} , mFrameSize {0} @@ -53,8 +54,8 @@ LottieAnimComponent::LottieAnimComponent() // Set component defaults. setOrigin(0.5f, 0.5f); - setSize(Renderer::getScreenWidth() * 0.2f, Renderer::getScreenHeight() * 0.2f); - setPosition(Renderer::getScreenWidth() * 0.3f, Renderer::getScreenHeight() * 0.3f); + setSize(mRenderer->getScreenWidth() * 0.2f, mRenderer->getScreenHeight() * 0.2f); + setPosition(mRenderer->getScreenWidth() * 0.3f, mRenderer->getScreenHeight() * 0.3f); setDefaultZIndex(10.0f); setZIndex(10.0f); } @@ -455,10 +456,10 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans) } } - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (Settings::getInstance()->getBool("DebugImage")) - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); + mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); if (mTexture->getSize().x != 0.0f) { mTexture->bind(); @@ -482,6 +483,6 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans) vertices->convertBGRAToRGBA = true; // Render it. - Renderer::drawTriangleStrips(&vertices[0], 4); + mRenderer->drawTriangleStrips(&vertices[0], 4); } } diff --git a/es-core/src/components/LottieAnimComponent.h b/es-core/src/components/LottieAnimComponent.h index 3d194b4fe..1949488f1 100644 --- a/es-core/src/components/LottieAnimComponent.h +++ b/es-core/src/components/LottieAnimComponent.h @@ -48,6 +48,7 @@ public: private: void render(const glm::mat4& parentTrans) override; + Renderer* mRenderer; std::shared_ptr mTexture; std::vector mPictureRGBA; std::unordered_map> mFrameCache; diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp index 907ed451c..582c37bf8 100644 --- a/es-core/src/components/NinePatchComponent.cpp +++ b/es-core/src/components/NinePatchComponent.cpp @@ -16,7 +16,8 @@ NinePatchComponent::NinePatchComponent(const std::string& path, unsigned int edgeColor, unsigned int centerColor) - : mVertices {nullptr} + : mRenderer {Renderer::getInstance()} + , mVertices {nullptr} , mPath {path} , mCornerSize {16.0f, 16.0f} , mSharpCorners {false} @@ -131,10 +132,10 @@ void NinePatchComponent::render(const glm::mat4& parentTrans) glm::mat4 trans {parentTrans * getTransform()}; if (mTexture && mVertices != nullptr) { - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); mVertices->opacity = mOpacity; mTexture->bind(); - Renderer::drawTriangleStrips(&mVertices[0], 6 * 9); + mRenderer->drawTriangleStrips(&mVertices[0], 6 * 9); } renderChildren(trans); diff --git a/es-core/src/components/NinePatchComponent.h b/es-core/src/components/NinePatchComponent.h index 2b05d7998..a989e871a 100644 --- a/es-core/src/components/NinePatchComponent.h +++ b/es-core/src/components/NinePatchComponent.h @@ -62,6 +62,7 @@ private: void buildVertices(); void updateColors(); + Renderer* mRenderer; Renderer::Vertex* mVertices; std::string mPath; diff --git a/es-core/src/components/RatingComponent.cpp b/es-core/src/components/RatingComponent.cpp index 9f4c9625b..b7e865d65 100644 --- a/es-core/src/components/RatingComponent.cpp +++ b/es-core/src/components/RatingComponent.cpp @@ -14,7 +14,8 @@ #include "resources/TextureResource.h" RatingComponent::RatingComponent(bool colorizeChanges) - : mColorOriginalValue {DEFAULT_COLORSHIFT} + : mRenderer {Renderer::getInstance()} + , mColorOriginalValue {DEFAULT_COLORSHIFT} , mColorChangedValue {DEFAULT_COLORSHIFT} , mColorShift {DEFAULT_COLORSHIFT} , mColorShiftEnd {DEFAULT_COLORSHIFT} @@ -152,12 +153,12 @@ void RatingComponent::render(const glm::mat4& parentTrans) glm::mat4 trans {parentTrans * getTransform()}; - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (mOpacity > 0.0f) { if (Settings::getInstance()->getBool("DebugImage")) { - Renderer::drawRect(0.0f, 0.0f, mSize.y * NUM_RATING_STARS, mSize.y, 0xFF000033, - 0xFF000033); + mRenderer->drawRect(0.0f, 0.0f, mSize.y * NUM_RATING_STARS, mSize.y, 0xFF000033, + 0xFF000033); } if (mUnfilledTexture->bind()) { @@ -167,16 +168,16 @@ void RatingComponent::render(const glm::mat4& parentTrans) (mUnfilledColor & 0xFFFFFF00) + (mVertices[i].color & 0x000000FF); } - Renderer::drawTriangleStrips(&mVertices[4], 4); - Renderer::bindTexture(0); + mRenderer->drawTriangleStrips(&mVertices[4], 4); + mRenderer->bindTexture(0); if (mUnfilledColor != mColorShift) updateColors(); } if (mFilledTexture->bind()) { - Renderer::drawTriangleStrips(&mVertices[0], 4); - Renderer::bindTexture(0); + mRenderer->drawTriangleStrips(&mVertices[0], 4); + mRenderer->bindTexture(0); } } diff --git a/es-core/src/components/RatingComponent.h b/es-core/src/components/RatingComponent.h index 98ce702c8..61c038973 100644 --- a/es-core/src/components/RatingComponent.h +++ b/es-core/src/components/RatingComponent.h @@ -52,6 +52,7 @@ private: void updateVertices(); void updateColors(); + Renderer* mRenderer; float mValue; int mOriginalValue; unsigned int mColorOriginalValue; diff --git a/es-core/src/components/ScrollableContainer.cpp b/es-core/src/components/ScrollableContainer.cpp index 7ea5edc0e..afd6fab1f 100644 --- a/es-core/src/components/ScrollableContainer.cpp +++ b/es-core/src/components/ScrollableContainer.cpp @@ -15,7 +15,8 @@ #include "resources/Font.h" ScrollableContainer::ScrollableContainer() - : mScrollPos {0.0f, 0.0f} + : mRenderer {Renderer::getInstance()} + , mScrollPos {0.0f, 0.0f} , mScrollDir {0.0f, 0.0f} , mClipSpacing {0.0f} , mAutoScrollDelay {0} @@ -233,11 +234,11 @@ void ScrollableContainer::render(const glm::mat4& parentTrans) clipPos.y += static_cast(mClipSpacing); clipDim.y -= static_cast(mClipSpacing); - Renderer::pushClipRect(clipPos, clipDim); + mRenderer->pushClipRect(clipPos, clipDim); trans = glm::translate(trans, -glm::vec3 {mScrollPos.x, mScrollPos.y, 0.0f}); - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); GuiComponent::renderChildren(trans); - Renderer::popClipRect(); + mRenderer->popClipRect(); } diff --git a/es-core/src/components/ScrollableContainer.h b/es-core/src/components/ScrollableContainer.h index 4201810d5..58dab3d08 100644 --- a/es-core/src/components/ScrollableContainer.h +++ b/es-core/src/components/ScrollableContainer.h @@ -18,6 +18,7 @@ #define AUTO_SCROLL_SPEED 4.0f #include "GuiComponent.h" +#include "renderers/Renderer.h" class ScrollableContainer : public GuiComponent { @@ -42,6 +43,7 @@ public: void render(const glm::mat4& parentTrans) override; private: + Renderer* mRenderer; glm::vec2 mScrollPos; glm::vec2 mScrollDir; diff --git a/es-core/src/components/SliderComponent.cpp b/es-core/src/components/SliderComponent.cpp index a84034fa0..670a67cc2 100644 --- a/es-core/src/components/SliderComponent.cpp +++ b/es-core/src/components/SliderComponent.cpp @@ -15,7 +15,8 @@ #define MOVE_REPEAT_RATE 40 SliderComponent::SliderComponent(float min, float max, float increment, const std::string& suffix) - : mMin {min} + : mRenderer {Renderer::getInstance()} + , mMin {min} , mMax {max} , mSingleIncrement {increment} , mMoveRate {0.0f} @@ -77,14 +78,14 @@ void SliderComponent::update(int deltaTime) void SliderComponent::render(const glm::mat4& parentTrans) { glm::mat4 trans {parentTrans * getTransform()}; - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (Settings::getInstance()->getBool("DebugText")) { - Renderer::drawRect( + mRenderer->drawRect( mSize.x - mTextCache->metrics.size.x, (mSize.y - mTextCache->metrics.size.y) / 2.0f, mTextCache->metrics.size.x, mTextCache->metrics.size.y, 0x0000FF33, 0x0000FF33); - Renderer::drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, mTextCache->metrics.size.x, - mSize.y, 0x00000033, 0x00000033); + mRenderer->drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, mTextCache->metrics.size.x, + mSize.y, 0x00000033, 0x00000033); } float width {mSize.x - mKnob.getSize().x - @@ -97,8 +98,8 @@ void SliderComponent::render(const glm::mat4& parentTrans) mFont->renderTextCache(mTextCache.get()); // Render bar. - Renderer::drawRect(mKnob.getSize().x / 2.0f, mSize.y / 2.0f - mBarHeight / 2.0f, width, - mBarHeight, 0x777777FF, 0x777777FF); + mRenderer->drawRect(mKnob.getSize().x / 2.0f, mSize.y / 2.0f - mBarHeight / 2.0f, width, + mBarHeight, 0x777777FF, 0x777777FF); // Render knob. mKnob.render(trans); diff --git a/es-core/src/components/SliderComponent.h b/es-core/src/components/SliderComponent.h index 3c6fde6b1..a0204b2d1 100644 --- a/es-core/src/components/SliderComponent.h +++ b/es-core/src/components/SliderComponent.h @@ -40,6 +40,7 @@ public: private: void onValueChanged(); + Renderer* mRenderer; float mMin, mMax; float mValue; float mSingleIncrement; diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index 77307920c..2973b5a00 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -14,6 +14,7 @@ TextComponent::TextComponent() : mFont {Font::get(FONT_SIZE_MEDIUM)} + , mRenderer {Renderer::getInstance()} , mColor {0x000000FF} , mBgColor {0x00000000} , mColorOpacity {1.0f} @@ -39,6 +40,7 @@ TextComponent::TextComponent(const std::string& text, glm::vec2 size, unsigned int bgcolor) : mFont {nullptr} + , mRenderer {Renderer::getInstance()} , mColor {0x000000FF} , mBgColor {0x00000000} , mColorOpacity {1.0f} @@ -161,11 +163,11 @@ void TextComponent::render(const glm::mat4& parentTrans) return; glm::mat4 trans {parentTrans * getTransform()}; - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (mRenderBackground) - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, mBgColor, mBgColor, false, - mOpacity * mThemeOpacity, mDimming); + mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, mBgColor, mBgColor, false, + mOpacity * mThemeOpacity, mDimming); if (mTextCache) { const glm::vec2& textSize {mTextCache->metrics.size}; @@ -191,29 +193,29 @@ void TextComponent::render(const glm::mat4& parentTrans) // Draw the "textbox" area, what we are aligned within. if (Settings::getInstance()->getBool("DebugText")) - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x0000FF33, 0x0000FF33); + mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x0000FF33, 0x0000FF33); trans = glm::translate(trans, off); - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); // Draw the text area, where the text actually is located. if (Settings::getInstance()->getBool("DebugText")) { switch (mHorizontalAlignment) { case ALIGN_LEFT: { - Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x, - mTextCache->metrics.size.y, 0x00000033, 0x00000033); + mRenderer->drawRect(0.0f, 0.0f, mTextCache->metrics.size.x, + mTextCache->metrics.size.y, 0x00000033, 0x00000033); break; } case ALIGN_CENTER: { - Renderer::drawRect((mSize.x - mTextCache->metrics.size.x) / 2.0f, 0.0f, - mTextCache->metrics.size.x, mTextCache->metrics.size.y, - 0x00000033, 0x00000033); + mRenderer->drawRect((mSize.x - mTextCache->metrics.size.x) / 2.0f, 0.0f, + mTextCache->metrics.size.x, mTextCache->metrics.size.y, + 0x00000033, 0x00000033); break; } case ALIGN_RIGHT: { - Renderer::drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, - mTextCache->metrics.size.x, mTextCache->metrics.size.y, - 0x00000033, 0x00000033); + mRenderer->drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, + mTextCache->metrics.size.x, mTextCache->metrics.size.y, + 0x00000033, 0x00000033); break; } default: { diff --git a/es-core/src/components/TextComponent.h b/es-core/src/components/TextComponent.h index 23b7a150f..67f09c889 100644 --- a/es-core/src/components/TextComponent.h +++ b/es-core/src/components/TextComponent.h @@ -89,6 +89,7 @@ private: void calculateExtent(); void onColorChanged(); + Renderer* mRenderer; unsigned int mColor; unsigned int mBgColor; float mColorOpacity; diff --git a/es-core/src/components/TextEditComponent.cpp b/es-core/src/components/TextEditComponent.cpp index 73d961ce5..ae2a72399 100644 --- a/es-core/src/components/TextEditComponent.cpp +++ b/es-core/src/components/TextEditComponent.cpp @@ -19,7 +19,8 @@ #define BLINKTIME 1000 TextEditComponent::TextEditComponent() - : mFocused {false} + : mRenderer {Renderer::getInstance()} + , mFocused {false} , mEditing {false} , mCursor {0} , mBlinkTime {0} @@ -304,16 +305,16 @@ void TextEditComponent::render(const glm::mat4& parentTrans) glm::ivec2 clipDim {static_cast(dimScaled.x - trans[3].x), static_cast(dimScaled.y - trans[3].y)}; - Renderer::pushClipRect(clipPos, clipDim); + mRenderer->pushClipRect(clipPos, clipDim); trans = glm::translate(trans, glm::vec3 {-mScrollOffset.x, -mScrollOffset.y, 0.0f}); - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); if (mTextCache) mFont->renderTextCache(mTextCache.get()); // Pop the clip early to allow the cursor to be drawn outside of the "text area". - Renderer::popClipRect(); + mRenderer->popClipRect(); // Draw cursor. glm::vec2 cursorPos; @@ -328,15 +329,15 @@ void TextEditComponent::render(const glm::mat4& parentTrans) float cursorHeight = mFont->getHeight() * 0.8f; if (!mEditing) { - Renderer::drawRect(cursorPos.x, cursorPos.y + (mFont->getHeight() - cursorHeight) / 2.0f, - 2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0xC7C7C7FF, - 0xC7C7C7FF); + mRenderer->drawRect(cursorPos.x, cursorPos.y + (mFont->getHeight() - cursorHeight) / 2.0f, + 2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0xC7C7C7FF, + 0xC7C7C7FF); } if (mEditing && mBlinkTime < BLINKTIME / 2) { - Renderer::drawRect(cursorPos.x, cursorPos.y + (mFont->getHeight() - cursorHeight) / 2.0f, - 2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0x777777FF, - 0x777777FF); + mRenderer->drawRect(cursorPos.x, cursorPos.y + (mFont->getHeight() - cursorHeight) / 2.0f, + 2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0x777777FF, + 0x777777FF); } } diff --git a/es-core/src/components/TextEditComponent.h b/es-core/src/components/TextEditComponent.h index 4587be988..e79fdf291 100644 --- a/es-core/src/components/TextEditComponent.h +++ b/es-core/src/components/TextEditComponent.h @@ -56,6 +56,7 @@ private: glm::vec2 getTextAreaPos() const; glm::vec2 getTextAreaSize() const; + Renderer* mRenderer; std::string mText; std::string mTextOrig; bool mFocused; diff --git a/es-core/src/components/TextListComponent.h b/es-core/src/components/TextListComponent.h index 74b4d6636..0a7d9526b 100644 --- a/es-core/src/components/TextListComponent.h +++ b/es-core/src/components/TextListComponent.h @@ -135,6 +135,7 @@ protected: void onCursorChanged(const CursorState& state) override; private: + Renderer* mRenderer; int mLoopOffset; int mLoopOffset2; int mLoopTime; @@ -164,6 +165,7 @@ private: template TextListComponent::TextListComponent() { + mRenderer = Renderer::getInstance(); mLoopOffset = 0; mLoopOffset2 = 0; mLoopTime = 0; @@ -232,17 +234,17 @@ template void TextListComponent::render(const glm::mat4& parentT mSelectorImage.render(trans); } else { - Renderer::setMatrix(trans); - Renderer::drawRect(0.0f, (mCursor - startEntry) * entrySize + mSelectorOffsetY, mSize.x, - mSelectorHeight, mSelectorColor, mSelectorColorEnd, - mSelectorColorGradientHorizontal); + mRenderer->setMatrix(trans); + mRenderer->drawRect(0.0f, (mCursor - startEntry) * entrySize + mSelectorOffsetY, + mSize.x, mSelectorHeight, mSelectorColor, mSelectorColorEnd, + mSelectorColorGradientHorizontal); } } if (Settings::getInstance()->getBool("DebugText")) { - Renderer::drawRect(mHorizontalMargin, 0.0f, mSize.x - mHorizontalMargin * 2.0f, mSize.y, - 0x00000033, 0x00000033); - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x0000FF33, 0x0000FF33); + mRenderer->drawRect(mHorizontalMargin, 0.0f, mSize.x - mHorizontalMargin * 2.0f, mSize.y, + 0x00000033, 0x00000033); + mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x0000FF33, 0x0000FF33); } // Clip to inside margins. @@ -250,7 +252,7 @@ template void TextListComponent::render(const glm::mat4& parentT dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x; dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y; - Renderer::pushClipRect( + mRenderer->pushClipRect( glm::ivec2 {static_cast(std::round(trans[3].x + mHorizontalMargin)), static_cast(std::round(trans[3].y))}, glm::ivec2 {static_cast(std::round(dim.x - mHorizontalMargin * 2.0f)), @@ -323,7 +325,7 @@ template void TextListComponent::render(const glm::mat4& parentT if (mLoopOffset == 0 && mLoopOffset2 == 0) mLoopScroll = false; - Renderer::setMatrix(drawTrans); + mRenderer->setMatrix(drawTrans); font->renderTextCache(entry.data.textCache.get()); // Render currently selected row again if text is moved far enough for it to repeat. @@ -332,12 +334,12 @@ template void TextListComponent::render(const glm::mat4& parentT drawTrans = trans; drawTrans = glm::translate( drawTrans, offset - glm::vec3 {static_cast(mLoopOffset2), 0.0f, 0.0f}); - Renderer::setMatrix(drawTrans); + mRenderer->setMatrix(drawTrans); font->renderTextCache(entry.data.textCache.get()); } y += entrySize; } - Renderer::popClipRect(); + mRenderer->popClipRect(); List::listRenderTitleOverlay(trans); GuiComponent::renderChildren(trans); } diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index 056c0fa37..a3fb441b2 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -15,6 +15,8 @@ #include "Window.h" #include "resources/TextureResource.h" +#include + #include #include @@ -24,7 +26,8 @@ std::vector VideoFFmpegComponent::sHWDecodedVideos; std::vector VideoFFmpegComponent::sSWDecodedVideos; VideoFFmpegComponent::VideoFFmpegComponent() - : mRectangleOffset {0.0f, 0.0f} + : mRenderer {Renderer::getInstance()} + , mRectangleOffset {0.0f, 0.0f} , mFrameProcessingThread {nullptr} , mFormatContext {nullptr} , mVideoStream {nullptr} @@ -133,7 +136,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) if (mIsPlaying && mFormatContext) { Renderer::Vertex vertices[4]; - Renderer::setMatrix(parentTrans); + mRenderer->setMatrix(parentTrans); unsigned int rectColor {0x000000FF}; @@ -142,9 +145,9 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) // Render the black rectangle behind the video. if (mVideoRectangleCoords.size() == 4) { - Renderer::drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1], - mVideoRectangleCoords[2], mVideoRectangleCoords[3], // Line break. - rectColor, rectColor); + mRenderer->drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1], + mVideoRectangleCoords[2], mVideoRectangleCoords[3], // Line break. + rectColor, rectColor); } // This is needed to avoid a slight gap before the video starts playing. @@ -219,8 +222,8 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) } // Render it. - Renderer::setMatrix(trans); - Renderer::drawTriangleStrips(&vertices[0], 4); + mRenderer->setMatrix(trans); + mRenderer->drawTriangleStrips(&vertices[0], 4); } else { if (mVisible) diff --git a/es-core/src/components/VideoFFmpegComponent.h b/es-core/src/components/VideoFFmpegComponent.h index eb14f3c63..ee4226fcb 100644 --- a/es-core/src/components/VideoFFmpegComponent.h +++ b/es-core/src/components/VideoFFmpegComponent.h @@ -84,6 +84,7 @@ private: static void detectHWDecoder(); bool decoderInitHW(); + Renderer* mRenderer; static enum AVHWDeviceType sDeviceType; static enum AVPixelFormat sPixelFormat; static std::vector sSWDecodedVideos; diff --git a/es-core/src/guis/GuiInfoPopup.cpp b/es-core/src/guis/GuiInfoPopup.cpp index d7b8b596c..fafd06883 100644 --- a/es-core/src/guis/GuiInfoPopup.cpp +++ b/es-core/src/guis/GuiInfoPopup.cpp @@ -15,7 +15,8 @@ #include GuiInfoPopup::GuiInfoPopup(std::string message, int duration) - : mMessage {message} + : mRenderer {Renderer::getInstance()} + , mMessage {message} , mDuration {duration} , mAlpha {1.0f} , mRunning {true} @@ -78,7 +79,7 @@ void GuiInfoPopup::render(const glm::mat4& /*parentTrans*/) glm::mat4 trans {getTransform() * Renderer::getIdentity()}; if (mRunning && updateState()) { // If we're still supposed to be rendering it. - Renderer::setMatrix(trans); + mRenderer->setMatrix(trans); renderChildren(trans); } } diff --git a/es-core/src/guis/GuiInfoPopup.h b/es-core/src/guis/GuiInfoPopup.h index 9d11072f3..8be3daa98 100644 --- a/es-core/src/guis/GuiInfoPopup.h +++ b/es-core/src/guis/GuiInfoPopup.h @@ -10,6 +10,7 @@ #define ES_APP_GUIS_GUI_INFO_POPUP_H #include "GuiComponent.h" +#include "renderers/Renderer.h" class ComponentGrid; class NinePatchComponent; @@ -27,6 +28,7 @@ public: private: bool updateState(); + Renderer* mRenderer; ComponentGrid* mGrid; NinePatchComponent* mFrame; diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp index f5a143ea7..33655ed12 100644 --- a/es-core/src/renderers/Renderer.cpp +++ b/es-core/src/renderers/Renderer.cpp @@ -3,7 +3,7 @@ // EmulationStation Desktop Edition // Renderer.cpp // -// General rendering functions. +// Generic rendering functions. // #include "renderers/Renderer.h" @@ -12,468 +12,386 @@ #include "Log.h" #include "Settings.h" #include "Shader_GL21.h" +#include "renderers/Renderer_GL21.h" #include "resources/ResourceManager.h" -#include -#include - #if defined(_WIN64) #include #endif -namespace Renderer +Renderer* Renderer::getInstance() { - static std::stack clipStack; - static SDL_Window* sdlWindow {nullptr}; - static glm::mat4 mProjectionMatrix {}; - static glm::mat4 mProjectionMatrixRotated {}; - static int windowWidth {0}; - static int windowHeight {0}; - static int screenWidth {0}; - static int screenHeight {0}; - static int screenOffsetX {0}; - static int screenOffsetY {0}; - static bool screenRotated {0}; - static bool initialCursorState {1}; - // Screen resolution modifiers relative to the 1920x1080 reference. - static float screenHeightModifier {0.0f}; - static float screenWidthModifier {0.0f}; - static float screenAspectRatio {0.0f}; + static RendererOpenGL instance; + return &instance; +} - static void setIcon() - { - size_t width {0}; - size_t height {0}; - ResourceData resData { - ResourceManager::getInstance().getFileData(":/graphics/window_icon_256.png")}; - std::vector rawData { - ImageIO::loadFromMemoryRGBA32(resData.ptr.get(), resData.length, width, height)}; +void Renderer::setIcon() +{ + size_t width {0}; + size_t height {0}; + ResourceData resData { + ResourceManager::getInstance().getFileData(":/graphics/window_icon_256.png")}; + std::vector rawData { + ImageIO::loadFromMemoryRGBA32(resData.ptr.get(), resData.length, width, height)}; - if (!rawData.empty()) { - ImageIO::flipPixelsVert(rawData.data(), width, height); + if (!rawData.empty()) { + ImageIO::flipPixelsVert(rawData.data(), width, height); #if SDL_BYTEORDER == SDL_BIG_ENDIAN - unsigned int rmask {0xFF000000}; - unsigned int gmask {0x00FF0000}; - unsigned int bmask {0x0000FF00}; - unsigned int amask {0x000000FF}; + unsigned int rmask {0xFF000000}; + unsigned int gmask {0x00FF0000}; + unsigned int bmask {0x0000FF00}; + unsigned int amask {0x000000FF}; #else - unsigned int rmask {0x000000FF}; - unsigned int gmask {0x0000FF00}; - unsigned int bmask {0x00FF0000}; - unsigned int amask {0xFF000000}; + unsigned int rmask {0x000000FF}; + unsigned int gmask {0x0000FF00}; + unsigned int bmask {0x00FF0000}; + unsigned int amask {0xFF000000}; #endif - // Try creating SDL surface from logo data. - SDL_Surface* logoSurface {SDL_CreateRGBSurfaceFrom( - static_cast(rawData.data()), static_cast(width), - static_cast(height), 32, static_cast((width * 4)), rmask, gmask, bmask, - amask)}; + // Try creating SDL surface from logo data. + SDL_Surface* logoSurface {SDL_CreateRGBSurfaceFrom( + static_cast(rawData.data()), static_cast(width), static_cast(height), + 32, static_cast((width * 4)), rmask, gmask, bmask, amask)}; - if (logoSurface != nullptr) { - SDL_SetWindowIcon(sdlWindow, logoSurface); - SDL_FreeSurface(logoSurface); - } + if (logoSurface != nullptr) { + SDL_SetWindowIcon(mSDLWindow, logoSurface); + SDL_FreeSurface(logoSurface); } } +} - static bool createWindow() - { - LOG(LogInfo) << "Creating window..."; +bool Renderer::createWindow() +{ + LOG(LogInfo) << "Creating window..."; - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - LOG(LogError) << "Couldn't initialize SDL: " << SDL_GetError(); - return false; - } + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + LOG(LogError) << "Couldn't initialize SDL: " << SDL_GetError(); + return false; + } - initialCursorState = (SDL_ShowCursor(0) != 0); + mInitialCursorState = (SDL_ShowCursor(0) != 0); - int displayIndex {Settings::getInstance()->getInt("DisplayIndex")}; - // Check that an invalid value has not been manually entered in the es_settings.xml file. - if (displayIndex != 1 && displayIndex != 2 && displayIndex != 3 && displayIndex != 4) { - Settings::getInstance()->setInt("DisplayIndex", 1); - displayIndex = 0; - } - else { - --displayIndex; - } + int displayIndex {Settings::getInstance()->getInt("DisplayIndex")}; + // Check that an invalid value has not been manually entered in the es_settings.xml file. + if (displayIndex != 1 && displayIndex != 2 && displayIndex != 3 && displayIndex != 4) { + Settings::getInstance()->setInt("DisplayIndex", 1); + displayIndex = 0; + } + else { + --displayIndex; + } - int availableDisplays = SDL_GetNumVideoDisplays(); - if (displayIndex > availableDisplays - 1) { - LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1) - << " does not exist, changing to display 1"; - displayIndex = 0; - } - else { - LOG(LogInfo) << "Using display: " << std::to_string(displayIndex + 1); - } + int availableDisplays = SDL_GetNumVideoDisplays(); + if (displayIndex > availableDisplays - 1) { + LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1) + << " does not exist, changing to display 1"; + displayIndex = 0; + } + else { + LOG(LogInfo) << "Using display: " << std::to_string(displayIndex + 1); + } - SDL_DisplayMode displayMode; - SDL_GetDesktopDisplayMode(displayIndex, &displayMode); + SDL_DisplayMode displayMode; + SDL_GetDesktopDisplayMode(displayIndex, &displayMode); #if defined(_WIN64) - // Tell Windows that we're DPI aware so that we can set a physical resolution and - // avoid any automatic DPI scaling. - SetProcessDPIAware(); - // We need to set the resolution based on the actual display bounds as the numbers - // returned by SDL_GetDesktopDisplayMode are calculated based on DPI scaling and - // therefore do not necessarily reflect the physical display resolution. - SDL_Rect displayBounds; - SDL_GetDisplayBounds(displayIndex, &displayBounds); - displayMode.w = displayBounds.w; - displayMode.h = displayBounds.h; + // Tell Windows that we're DPI aware so that we can set a physical resolution and + // avoid any automatic DPI scaling. + SetProcessDPIAware(); + // We need to set the resolution based on the actual display bounds as the numbers + // returned by SDL_GetDesktopDisplayMode are calculated based on DPI scaling and + // therefore do not necessarily reflect the physical display resolution. + SDL_Rect displayBounds; + SDL_GetDisplayBounds(displayIndex, &displayBounds); + displayMode.w = displayBounds.w; + displayMode.h = displayBounds.h; #endif - windowWidth = Settings::getInstance()->getInt("WindowWidth") ? - Settings::getInstance()->getInt("WindowWidth") : - displayMode.w; - windowHeight = Settings::getInstance()->getInt("WindowHeight") ? - Settings::getInstance()->getInt("WindowHeight") : - displayMode.h; - screenWidth = Settings::getInstance()->getInt("ScreenWidth") ? - Settings::getInstance()->getInt("ScreenWidth") : - windowWidth; - screenHeight = Settings::getInstance()->getInt("ScreenHeight") ? - Settings::getInstance()->getInt("ScreenHeight") : - windowHeight; - screenOffsetX = Settings::getInstance()->getInt("ScreenOffsetX") ? - Settings::getInstance()->getInt("ScreenOffsetX") : - 0; - screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ? - Settings::getInstance()->getInt("ScreenOffsetY") : - 0; - screenRotated = Settings::getInstance()->getBool("ScreenRotate"); + mWindowWidth = Settings::getInstance()->getInt("WindowWidth") ? + Settings::getInstance()->getInt("WindowWidth") : + displayMode.w; + mWindowHeight = Settings::getInstance()->getInt("WindowHeight") ? + Settings::getInstance()->getInt("WindowHeight") : + displayMode.h; + sScreenWidth = Settings::getInstance()->getInt("ScreenWidth") ? + Settings::getInstance()->getInt("ScreenWidth") : + mWindowWidth; + sScreenHeight = Settings::getInstance()->getInt("ScreenHeight") ? + Settings::getInstance()->getInt("ScreenHeight") : + mWindowHeight; + mScreenOffsetX = Settings::getInstance()->getInt("ScreenOffsetX") ? + Settings::getInstance()->getInt("ScreenOffsetX") : + 0; + mScreenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ? + Settings::getInstance()->getInt("ScreenOffsetY") : + 0; + mScreenRotated = Settings::getInstance()->getBool("ScreenRotate"); - // Prevent the application window from minimizing when switching windows (when launching - // games or when manually switching windows using the task switcher). - SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); + // Prevent the application window from minimizing when switching windows (when launching + // games or when manually switching windows using the task switcher). + SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); #if defined(__unix__) - // Disabling desktop composition can lead to better framerates and a more fluid user - // interface, but with some drivers it can cause strange behaviors when returning to - // the desktop. - if (Settings::getInstance()->getBool("DisableComposition")) - SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "1"); - else - SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); + // Disabling desktop composition can lead to better framerates and a more fluid user + // interface, but with some drivers it can cause strange behaviors when returning to + // the desktop. + if (Settings::getInstance()->getBool("DisableComposition")) + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "1"); + else + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); #endif - bool userResolution = false; - // Check if the user has changed the resolution from the command line. - if (windowWidth != displayMode.w || windowHeight != displayMode.h) - userResolution = true; + bool userResolution = false; + // Check if the user has changed the resolution from the command line. + if (mWindowWidth != displayMode.w || mWindowHeight != displayMode.h) + userResolution = true; - unsigned int windowFlags; - setupWindow(); + unsigned int windowFlags; + setup(); #if defined(_WIN64) - // For Windows we use SDL_WINDOW_BORDERLESS as "real" full screen doesn't work properly. - // The borderless mode seems to behave well and it's almost completely seamless, especially - // with a hidden taskbar. - if (!userResolution) - windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_OPENGL; - else - // If the resolution has been manually set from the command line, then keep the border. - windowFlags = SDL_WINDOW_OPENGL; + // For Windows we use SDL_WINDOW_BORDERLESS as "real" full screen doesn't work properly. + // The borderless mode seems to behave well and it's almost completely seamless, especially + // with a hidden taskbar. + if (!userResolution) + windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_OPENGL; + else + // If the resolution has been manually set from the command line, then keep the border. + windowFlags = SDL_WINDOW_OPENGL; #elif defined(__APPLE__) - // Not sure if this could be a useful setting. - // SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0"); + // Not sure if this could be a useful setting. + // SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0"); - // The SDL_WINDOW_BORDERLESS mode seems to be the only mode that somehow works on macOS - // as a real fullscreen mode will do lots of weird stuff like preventing window switching - // or refusing to let emulators run at all. SDL_WINDOW_FULLSCREEN_DESKTOP almost works, but - // it "shuffles" windows when starting the emulator and won't return properly when the game - // has exited. With SDL_WINDOW_BORDERLESS some emulators (like RetroArch) have to be - // configured to run in fullscreen mode or switching to its window will not work, but - // apart from that this mode works fine. - if (!userResolution) - windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL; - else - windowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL; + // The SDL_WINDOW_BORDERLESS mode seems to be the only mode that somehow works on macOS + // as a real fullscreen mode will do lots of weird stuff like preventing window switching + // or refusing to let emulators run at all. SDL_WINDOW_FULLSCREEN_DESKTOP almost works, but + // it "shuffles" windows when starting the emulator and won't return properly when the game + // has exited. With SDL_WINDOW_BORDERLESS some emulators (like RetroArch) have to be + // configured to run in fullscreen mode or switching to its window will not work, but + // apart from that this mode works fine. + if (!userResolution) + windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL; + else + windowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL; #else - if (!userResolution) - windowFlags = SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_OPENGL; - else - windowFlags = SDL_WINDOW_OPENGL; + if (!userResolution) + windowFlags = SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_OPENGL; + else + windowFlags = SDL_WINDOW_OPENGL; #endif - if ((sdlWindow = - SDL_CreateWindow("EmulationStation", SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), - SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), windowWidth, - windowHeight, windowFlags)) == nullptr) { - LOG(LogError) << "Couldn't create SDL window. " << SDL_GetError(); - return false; - } + if ((mSDLWindow = + SDL_CreateWindow("EmulationStation", SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), + SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), mWindowWidth, + mWindowHeight, windowFlags)) == nullptr) { + LOG(LogError) << "Couldn't create SDL window. " << SDL_GetError(); + return false; + } #if defined(__APPLE__) - // The code below is required as the high DPI scaling on macOS is very bizarre and is - // measured in "points" rather than pixels (even though the naming convention sure looks - // like pixels). For example there could be a 1920x1080 entry in the OS display settings - // that actually corresponds to something like 3840x2160 pixels while at the same time - // there is a separate 1080p entry which corresponds to a "real" 1920x1080 resolution. - // Therefore the --resolution flag results in different things depending on whether a high - // DPI screen is used. E.g. 1280x720 on a 4K display would actually end up as 2560x1440 - // which is incredibly strange. No point in struggling with this strangeness though, - // instead we simply indicate the physical pixel dimensions in parenthesis in the log - // file and make sure to double the window and screen sizes in case of a high DPI - // display so that the full application window is used for rendering. - int width = 0; - SDL_GL_GetDrawableSize(sdlWindow, &width, nullptr); - int scaleFactor = static_cast(width / windowWidth); + // The code below is required as the high DPI scaling on macOS is very bizarre and is + // measured in "points" rather than pixels (even though the naming convention sure looks + // like pixels). For example there could be a 1920x1080 entry in the OS display settings + // that actually corresponds to something like 3840x2160 pixels while at the same time + // there is a separate 1080p entry which corresponds to a "real" 1920x1080 resolution. + // Therefore the --resolution flag results in different things depending on whether a high + // DPI screen is used. E.g. 1280x720 on a 4K display would actually end up as 2560x1440 + // which is incredibly strange. No point in struggling with this strangeness though, + // instead we simply indicate the physical pixel dimensions in parenthesis in the log + // file and make sure to double the window and screen sizes in case of a high DPI + // display so that the full application window is used for rendering. + int width = 0; + SDL_GL_GetDrawableSize(mSDLWindow, &width, nullptr); + int scaleFactor = static_cast(width / mWindowWidth); - LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" - << std::to_string(displayMode.h) << " (physical resolution " - << std::to_string(displayMode.w * scaleFactor) << "x" - << std::to_string(displayMode.h * scaleFactor) << ")"; - LOG(LogInfo) << "Display refresh rate: " << std::to_string(displayMode.refresh_rate) - << " Hz"; - LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" - << std::to_string(windowHeight) << " (physical resolution " - << std::to_string(windowWidth * scaleFactor) << "x" - << std::to_string(windowHeight * scaleFactor) << ")"; + LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" + << std::to_string(displayMode.h) << " (physical resolution " + << std::to_string(displayMode.w * scaleFactor) << "x" + << std::to_string(displayMode.h * scaleFactor) << ")"; + LOG(LogInfo) << "Display refresh rate: " << std::to_string(displayMode.refresh_rate) << " Hz"; + LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(mWindowWidth) << "x" + << std::to_string(mWindowHeight) << " (physical resolution " + << std::to_string(mWindowWidth * scaleFactor) << "x" + << std::to_string(mWindowHeight * scaleFactor) << ")"; - windowWidth *= scaleFactor; - windowHeight *= scaleFactor; - screenWidth *= scaleFactor; - screenHeight *= scaleFactor; + mWindowWidth *= scaleFactor; + mWindowHeight *= scaleFactor; + sScreenWidth *= scaleFactor; + sScreenHeight *= scaleFactor; #else - LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" - << std::to_string(displayMode.h); - LOG(LogInfo) << "Display refresh rate: " << std::to_string(displayMode.refresh_rate) - << " Hz"; - LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" - << std::to_string(windowHeight); + LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" + << std::to_string(displayMode.h); + LOG(LogInfo) << "Display refresh rate: " << std::to_string(displayMode.refresh_rate) << " Hz"; + LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(mWindowWidth) << "x" + << std::to_string(mWindowHeight); #endif - screenHeightModifier = static_cast(screenHeight) / 1080.0f; - screenWidthModifier = static_cast(screenWidth) / 1920.0f; - screenAspectRatio = static_cast(screenWidth) / static_cast(screenHeight); + sScreenHeightModifier = static_cast(sScreenHeight) / 1080.0f; + sScreenWidthModifier = static_cast(sScreenWidth) / 1920.0f; + sScreenAspectRatio = static_cast(sScreenWidth) / static_cast(sScreenHeight); - LOG(LogInfo) << "Setting up OpenGL..."; + LOG(LogInfo) << "Setting up OpenGL..."; - if (!createContext()) - return false; + if (!createContext()) + return false; - setIcon(); - setSwapInterval(); + setIcon(); + setSwapInterval(); #if defined(_WIN64) - // It seems as if Windows needs this to avoid a brief white screen flash on startup. - // Possibly this is driver-specific rather than OS-specific. There is additional code - // in init() to work around the white screen flash issue on all operating systems. - swapBuffers(); + // It seems as if Windows needs this to avoid a brief white screen flash on startup. + // Possibly this is driver-specific rather than OS-specific. There is additional code + // in init() to work around the white screen flash issue on all operating systems. + swapBuffers(); #endif - LOG(LogInfo) << "Loading shaders..."; + return loadShaders(); +} - std::vector shaderFiles; - shaderFiles.push_back(":/shaders/glsl/core.glsl"); - shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl"); - shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl"); - shaderFiles.push_back(":/shaders/glsl/scanlines.glsl"); +void Renderer::destroyWindow() +{ + destroyContext(); + SDL_DestroyWindow(mSDLWindow); - for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); ++it) { - Shader* loadShader = new Shader(); + mSDLWindow = nullptr; - loadShader->loadShaderFile(*it, GL_VERTEX_SHADER); - loadShader->loadShaderFile(*it, GL_FRAGMENT_SHADER); + SDL_ShowCursor(mInitialCursorState); + SDL_Quit(); +} - if (!loadShader->createProgram()) { - LOG(LogError) << "Could not create shader program."; - return false; - } +bool Renderer::init() +{ + if (!createWindow()) + return false; - sShaderProgramVector.push_back(loadShader); - } + glm::mat4 projection {getIdentity()}; + Rect viewport {0, 0, 0, 0}; - return true; + viewport.x = mWindowWidth - mScreenOffsetX - sScreenWidth; + 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}); + + 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()), + static_cast(getScreenHeight()), 0x000000FF, 0x000000FF); + swapBuffers(); + + return true; +} + +void Renderer::deinit() +{ + // Destroy the window. + destroyWindow(); +} + +void Renderer::pushClipRect(const glm::ivec2& pos, const glm::ivec2& size) +{ + Rect box {pos.x, pos.y, size.x, size.y}; + + if (box.w == 0) + box.w = sScreenWidth - box.x; + 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); + } + else { + box = Rect(mScreenOffsetX + box.x, mScreenOffsetY + box.y, box.w, box.h); } - static void destroyWindow() - { - for (auto it = sShaderProgramVector.cbegin(); it != sShaderProgramVector.cend(); ++it) - delete *it; - - destroyContext(); - SDL_DestroyWindow(sdlWindow); - - sdlWindow = nullptr; - - SDL_ShowCursor(initialCursorState); - SDL_Quit(); + // Make sure the box fits within mClipStack.top(), and clip further accordingly. + if (mClipStack.size()) { + const Rect& top {mClipStack.top()}; + if (top.x > box.x) + box.x = top.x; + if (top.y > box.y) + box.y = top.y; + if ((top.x + top.w) < (box.x + box.w)) + box.w = (top.x + top.w) - box.x; + if ((top.y + top.h) < (box.y + box.h)) + box.h = (top.y + top.h) - box.y; } - bool init() - { - if (!createWindow()) - return false; + if (box.w < 0) + box.w = 0; + if (box.h < 0) + box.h = 0; - glm::mat4 projection {getIdentity()}; - Rect viewport {0, 0, 0, 0}; + mClipStack.push(box); - viewport.x = windowWidth - screenOffsetX - screenWidth; - viewport.y = windowHeight - screenOffsetY - screenHeight; - viewport.w = screenWidth; - viewport.h = screenHeight; - projection = glm::ortho(0.0f, static_cast(screenWidth), - static_cast(screenHeight), 0.0f, -1.0f, 1.0f); - projection = glm::rotate(projection, glm::radians(180.0f), {0.0f, 0.0f, 1.0f}); - mProjectionMatrixRotated = - glm::translate(projection, {screenWidth * -1.0f, screenHeight * -1.0f, 0.0f}); + setScissor(box); +} - viewport.x = screenOffsetX; - viewport.y = screenOffsetY; - viewport.w = screenWidth; - viewport.h = screenHeight; - mProjectionMatrix = glm::ortho(0.0f, static_cast(screenWidth), - static_cast(screenHeight), 0.0f, -1.0f, 1.0f); - - // This is required to avoid a brief white screen flash during startup on some systems. - Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); - swapBuffers(); - - return true; +void Renderer::popClipRect() +{ + if (mClipStack.empty()) { + LOG(LogError) << "Tried to popClipRect while the stack was empty"; + return; } - void deinit() - { - // Destroy the window. - destroyWindow(); - } + mClipStack.pop(); - void pushClipRect(const glm::ivec2& pos, const glm::ivec2& size) - { - Rect box(pos.x, pos.y, size.x, size.y); + if (mClipStack.empty()) + setScissor(Rect(0, 0, 0, 0)); + else + setScissor(mClipStack.top()); +} - if (box.w == 0) - box.w = screenWidth - box.x; - if (box.h == 0) - box.h = screenHeight - box.y; +void Renderer::drawRect(const float x, + const float y, + const float w, + const float h, + const unsigned int color, + const unsigned int colorEnd, + bool horizontalGradient, + const float opacity, + const float dimming, + const BlendFactor srcBlendFactorFactor, + const BlendFactor dstBlendFactorFactor) +{ + Vertex vertices[4]; - if (screenRotated) { - box = Rect(windowWidth - screenOffsetX - box.x - box.w, - windowHeight - screenOffsetY - box.y - box.h, box.w, box.h); - } - else { - box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h); - } + float wL {w}; + float hL {h}; - // Make sure the box fits within clipStack.top(), and clip further accordingly. - if (clipStack.size()) { - const Rect& top = clipStack.top(); - if (top.x > box.x) - box.x = top.x; - if (top.y > box.y) - box.y = top.y; - if ((top.x + top.w) < (box.x + box.w)) - box.w = (top.x + top.w) - box.x; - if ((top.y + top.h) < (box.y + box.h)) - box.h = (top.y + top.h) - box.y; - } + // If the width or height was scaled down to less than 1 pixel, then set it to + // 1 pixel so that it will still render on lower resolutions. + if (wL > 0.0f && wL < 1.0f) + wL = 1.0f; + if (hL > 0.0f && hL < 1.0f) + hL = 1.0f; - if (box.w < 0) - box.w = 0; - if (box.h < 0) - box.h = 0; + // clang-format off + vertices[0] = {{x, y }, {0.0f, 0.0f}, color}; + vertices[1] = {{x, y + hL}, {0.0f, 0.0f}, horizontalGradient ? color : colorEnd}; + vertices[2] = {{x + wL, y }, {0.0f, 0.0f}, horizontalGradient ? colorEnd : color}; + vertices[3] = {{x + wL, y + hL}, {0.0f, 0.0f}, colorEnd}; + // clang-format on - clipStack.push(box); + // Round vertices. + for (int i = 0; i < 4; ++i) + vertices[i].position = glm::round(vertices[i].position); - setScissor(box); - } + vertices->opacity = opacity; + vertices->dimming = dimming; - void popClipRect() - { - if (clipStack.empty()) { - LOG(LogError) << "Tried to popClipRect while the stack was empty"; - return; - } - - clipStack.pop(); - - if (clipStack.empty()) - setScissor(Rect(0, 0, 0, 0)); - else - setScissor(clipStack.top()); - } - - void drawRect(const float x, - const float y, - const float w, - const float h, - const unsigned int color, - const unsigned int colorEnd, - bool horizontalGradient, - const float opacity, - const float dimming, - const Blend::Factor srcBlendFactor, - const Blend::Factor dstBlendFactor) - { - Vertex vertices[4]; - - float wL {w}; - float hL {h}; - - // If the width or height was scaled down to less than 1 pixel, then set it to - // 1 pixel so that it will still render on lower resolutions. - if (wL > 0.0f && wL < 1.0f) - wL = 1.0f; - if (hL > 0.0f && hL < 1.0f) - hL = 1.0f; - - // clang-format off - vertices[0] = {{x, y }, {0.0f, 0.0f}, color}; - vertices[1] = {{x, y + hL}, {0.0f, 0.0f}, horizontalGradient ? color : colorEnd}; - vertices[2] = {{x + wL, y }, {0.0f, 0.0f}, horizontalGradient ? colorEnd : color}; - vertices[3] = {{x + wL, y + hL}, {0.0f, 0.0f}, colorEnd}; - // clang-format on - - // Round vertices. - for (int i = 0; i < 4; ++i) - vertices[i].position = glm::round(vertices[i].position); - - vertices->opacity = opacity; - vertices->dimming = dimming; - - bindTexture(0); - drawTriangleStrips(vertices, 4, srcBlendFactor, dstBlendFactor); - } - - Shader* getShaderProgram(unsigned int shaderID) - { - unsigned int index = 0; - - // Find the index in sShaderProgramVector by counting the number - // of shifts required to reach 0. - while (shaderID > 0) { - shaderID = shaderID >> 1; - ++index; - } - - if (sShaderProgramVector.size() > index - 1) - return sShaderProgramVector[index - 1]; - else - return nullptr; - } - - const glm::mat4& getProjectionMatrix() - { - if (screenRotated) - return mProjectionMatrixRotated; - else - return mProjectionMatrix; - } - const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrix; } - SDL_Window* getSDLWindow() { return sdlWindow; } - const float getWindowWidth() { return static_cast(windowWidth); } - const float getWindowHeight() { return static_cast(windowHeight); } - const float getScreenWidth() { return static_cast(screenWidth); } - const float getScreenHeight() { return static_cast(screenHeight); } - const float getScreenOffsetX() { return static_cast(screenOffsetX); } - const float getScreenOffsetY() { return static_cast(screenOffsetY); } - const bool getScreenRotated() { return screenRotated; } - const float getScreenWidthModifier() { return screenWidthModifier; } - const float getScreenHeightModifier() { return screenHeightModifier; } - const float getScreenAspectRatio() { return screenAspectRatio; } - -} // namespace Renderer + bindTexture(0); + drawTriangleStrips(vertices, 4, srcBlendFactorFactor, dstBlendFactorFactor); +} diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h index 2a72184d5..64b3f46c8 100644 --- a/es-core/src/renderers/Renderer.h +++ b/es-core/src/renderers/Renderer.h @@ -3,123 +3,25 @@ // EmulationStation Desktop Edition // Renderer.h // -// General rendering functions. +// Generic rendering functions. // #ifndef ES_CORE_RENDERER_RENDERER_H #define ES_CORE_RENDERER_RENDERER_H #include "Log.h" -#include "Shader_GL21.h" #include "utils/MathUtil.h" +#include #include #include struct SDL_Window; +class Shader; -namespace Renderer +class Renderer { - // clang-format off - const unsigned int SHADER_CORE {0x00000001}; - const unsigned int SHADER_BLUR_HORIZONTAL {0x00000002}; - const unsigned int SHADER_BLUR_VERTICAL {0x00000004}; - const unsigned int SHADER_SCANLINES {0x00000008}; - // clang-format on - - struct postProcessingParams { - float opacity; - float saturation; - float dimming; - bool convertBGRAToRGBA; - unsigned int blurPasses; - unsigned int shaders; - - postProcessingParams() - : opacity {1.0f} - , saturation {1.0f} - , dimming {1.0f} - , convertBGRAToRGBA {false} - , blurPasses {1} - , shaders {0} - { - } - }; - - static std::vector sShaderProgramVector; - static GLuint shaderFBO1 {0}; - static GLuint shaderFBO2 {0}; - static GLuint vertexBuffer1 {0}; - static GLuint vertexBuffer2 {0}; - // This is simply to get rid of some GCC false positive -Wunused-variable compiler warnings. - static GLuint shaderFBODummy1 {shaderFBO1}; - static GLuint shaderFBODummy2 {shaderFBO2}; - static GLuint vertexBufferDummy1 {vertexBuffer1}; - static GLuint vertexBufferDummy2 {vertexBuffer2}; - - static constexpr glm::mat4 getIdentity() { return glm::mat4 {1.0f}; } - static inline glm::mat4 mTrans {getIdentity()}; - -#if !defined(NDEBUG) -#define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function)) - - static inline void _GLCheckError(const std::string& _funcName) - { - const GLenum errorCode = glGetError(); - - if (errorCode != GL_NO_ERROR) { -#if defined(USE_OPENGLES) - LOG(LogError) << "OpenGL ES error: " << _funcName << " failed with error code: 0x" - << std::hex << errorCode; -#else - LOG(LogError) << "OpenGL error: " << _funcName << " failed with error code: 0x" - << std::hex << errorCode; -#endif - } - } -#else -#define GL_CHECK_ERROR(Function) (Function) -#endif - - namespace Blend - { - enum Factor { - ZERO, - ONE, - SRC_COLOR, - ONE_MINUS_SRC_COLOR, - SRC_ALPHA, - ONE_MINUS_SRC_ALPHA, - DST_COLOR, - ONE_MINUS_DST_COLOR, - DST_ALPHA, - ONE_MINUS_DST_ALPHA - }; - } - - namespace Texture - { - enum Type { - RGBA, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). - BGRA, - RED - }; - } - - struct Rect { - Rect(const int xValue, const int yValue, const int wValue, const int hValue) - : x(xValue) - , y(yValue) - , w(wValue) - , h(hValue) - { - } - int x; - int y; - int w; - int h; - }; - +public: struct Vertex { glm::vec2 position; glm::vec2 texture; @@ -142,6 +44,7 @@ namespace Renderer , shaders {0} { } + Vertex(const glm::vec2& position, const glm::vec2& textureCoord, const unsigned int color) : position(position) , texture(textureCoord) @@ -157,10 +60,71 @@ namespace Renderer } }; + enum class BlendFactor { + ZERO, + ONE, + SRC_COLOR, + ONE_MINUS_SRC_COLOR, + SRC_ALPHA, + ONE_MINUS_SRC_ALPHA, + DST_COLOR, + ONE_MINUS_DST_COLOR, + DST_ALPHA, + ONE_MINUS_DST_ALPHA + }; + + enum class TextureType { + RGBA, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). + BGRA, + RED + }; + + struct postProcessingParams { + float opacity; + float saturation; + float dimming; + bool convertBGRAToRGBA; + unsigned int blurPasses; + unsigned int shaders; + + postProcessingParams() + : opacity {1.0f} + , saturation {1.0f} + , dimming {1.0f} + , convertBGRAToRGBA {false} + , blurPasses {1} + , shaders {0} + { + } + }; + + struct Rect { + Rect(const int xValue, const int yValue, const int wValue, const int hValue) + : x(xValue) + , y(yValue) + , w(wValue) + , h(hValue) + { + } + int x; + int y; + int w; + int h; + }; + + static Renderer* getInstance(); + + void setIcon(); + bool createWindow(); + void destroyWindow(); bool init(); void deinit(); + + virtual bool loadShaders() = 0; + void pushClipRect(const glm::ivec2& pos, const glm::ivec2& size); void popClipRect(); + void drawRect(const float x, const float y, const float w, @@ -170,56 +134,87 @@ namespace Renderer bool horizontalGradient = false, const float opacity = 1.0, const float dimming = 1.0, - const Blend::Factor srcBlendFactor = Blend::SRC_ALPHA, - const Blend::Factor dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA); - SDL_Window* getSDLWindow(); - const float getWindowWidth(); - const float getWindowHeight(); - const float getScreenWidth(); - const float getScreenHeight(); - const float getScreenOffsetX(); - const float getScreenOffsetY(); - const bool getScreenRotated(); - const float getScreenWidthModifier(); - const float getScreenHeightModifier(); - const float getScreenAspectRatio(); + const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA, + const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA); - Shader* getShaderProgram(unsigned int shaderID); - const glm::mat4& getProjectionMatrix(); - const glm::mat4& getProjectionMatrixNormal(); - void shaderPostprocessing( + const glm::mat4& getProjectionMatrix() + { + if (mScreenRotated) + return mProjectionMatrixRotated; + else + return mProjectionMatrix; + } + const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrix; } + SDL_Window* getSDLWindow() { return mSDLWindow; } + const bool getScreenRotated() { return mScreenRotated; } + const float getWindowWidth() { return static_cast(mWindowWidth); } + const float getWindowHeight() { return static_cast(mWindowHeight); } + static const float getScreenWidth() { return static_cast(sScreenWidth); } + static const float getScreenHeight() { return static_cast(sScreenHeight); } + static const float getScreenWidthModifier() { return sScreenWidthModifier; } + static const float getScreenHeightModifier() { return sScreenHeightModifier; } + static const float getScreenAspectRatio() { return sScreenAspectRatio; } + + static constexpr glm::mat4 getIdentity() { return glm::mat4 {1.0f}; } + glm::mat4 mTrans {getIdentity()}; + + virtual void shaderPostprocessing( const unsigned int shaders, const Renderer::postProcessingParams& parameters = postProcessingParams(), - unsigned char* textureRGBA = nullptr); + unsigned char* textureRGBA = nullptr) = 0; - void setupWindow(); - bool createContext(); - void destroyContext(); - unsigned int createTexture(const Texture::Type type, - const bool linearMinify, - const bool linearMagnify, - const bool repeat, + virtual void setup() = 0; + virtual bool createContext() = 0; + virtual void destroyContext() = 0; + virtual unsigned int createTexture(const TextureType type, + const bool linearMinify, + const bool linearMagnify, + const bool repeat, + const unsigned int width, + const unsigned int height, + void* data) = 0; + virtual void destroyTexture(const unsigned int texture) = 0; + virtual void updateTexture(const unsigned int texture, + const TextureType type, + const unsigned int x, + const unsigned int y, const unsigned int width, const unsigned int height, - void* data); - void destroyTexture(const unsigned int texture); - void updateTexture(const unsigned int texture, - const Texture::Type type, - const unsigned int x, - const unsigned int y, - const unsigned int width, - const unsigned int height, - void* data); - void bindTexture(const unsigned int texture); - void drawTriangleStrips(const Vertex* vertices, - const unsigned int numVertices, - const Blend::Factor srcBlendFactor = Blend::SRC_ALPHA, - const Blend::Factor dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA); - void setMatrix(const glm::mat4& matrix); - void setScissor(const Rect& scissor); - void setSwapInterval(); - void swapBuffers(); + void* data) = 0; + virtual void bindTexture(const unsigned int texture) = 0; + virtual void drawTriangleStrips( + const Vertex* vertices, + const unsigned int numVertices, + const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA, + const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA) = 0; + virtual void setMatrix(const glm::mat4& matrix) = 0; + virtual void setScissor(const Rect& scissor) = 0; + virtual void setSwapInterval() = 0; + virtual void swapBuffers() = 0; -} // namespace Renderer + // clang-format off + static constexpr unsigned int SHADER_CORE {0x00000001}; + static constexpr unsigned int SHADER_BLUR_HORIZONTAL {0x00000002}; + static constexpr unsigned int SHADER_BLUR_VERTICAL {0x00000004}; + static constexpr unsigned int SHADER_SCANLINES {0x00000008}; + // clang-format on + + std::stack mClipStack; + SDL_Window* mSDLWindow {nullptr}; + glm::mat4 mProjectionMatrix {}; + glm::mat4 mProjectionMatrixRotated {}; + 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}; + // 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}; +}; #endif // ES_CORE_RENDERER_RENDERER_H diff --git a/es-core/src/renderers/Renderer_GL21.cpp b/es-core/src/renderers/Renderer_GL21.cpp index edeb845d4..db79df9e0 100644 --- a/es-core/src/renderers/Renderer_GL21.cpp +++ b/es-core/src/renderers/Renderer_GL21.cpp @@ -3,500 +3,573 @@ // EmulationStation Desktop Edition // Renderer_GL21.cpp // -// OpenGL / OpenGL ES renderer. +// OpenGL / OpenGL ES renderering functions. // +#include "renderers/Renderer_GL21.h" + #include "Settings.h" -#include "Shader_GL21.h" -#include "renderers/Renderer.h" #if defined(__APPLE__) #include #endif -namespace Renderer +RendererOpenGL::RendererOpenGL() noexcept + : mShaderFBO1 {0} + , mShaderFBO2 {0} + , mVertexBuffer1 {0} + , mVertexBuffer2 {0} + , mSDLContext {nullptr} + , mWhiteTexture {0} + , mPostProcTexture1 {0} + , mPostProcTexture2 {0} + , mCoreShader {nullptr} + , mBlurHorizontalShader {nullptr} + , mBlurVerticalShader {nullptr} + , mScanlinelShader {nullptr} + , mLastShader {nullptr} { - static SDL_GLContext sdlContext = nullptr; - static GLuint whiteTexture {0}; - static GLuint postProcTexture1 {0}; - static GLuint postProcTexture2 {0}; +} - inline GLenum convertBlendFactor(const Blend::Factor blendFactor) - { - // clang-format off - switch (blendFactor) { - case Blend::ZERO: { return GL_ZERO; } break; - case Blend::ONE: { return GL_ONE; } break; - case Blend::SRC_COLOR: { return GL_SRC_COLOR; } break; - case Blend::ONE_MINUS_SRC_COLOR: { return GL_ONE_MINUS_SRC_COLOR; } break; - case Blend::SRC_ALPHA: { return GL_SRC_ALPHA; } break; - case Blend::ONE_MINUS_SRC_ALPHA: { return GL_ONE_MINUS_SRC_ALPHA; } break; - case Blend::DST_COLOR: { return GL_DST_COLOR; } break; - case Blend::ONE_MINUS_DST_COLOR: { return GL_ONE_MINUS_DST_COLOR; } break; - case Blend::DST_ALPHA: { return GL_DST_ALPHA; } break; - case Blend::ONE_MINUS_DST_ALPHA: { return GL_ONE_MINUS_DST_ALPHA; } break; - default: { return GL_ZERO; } - } - // clang-format on +RendererOpenGL::~RendererOpenGL() +{ + for (auto it = mShaderProgramVector.cbegin(); it != mShaderProgramVector.cend(); ++it) + delete *it; +} + +RendererOpenGL* RendererOpenGL::getInstance() +{ + static RendererOpenGL instance; + return &instance; +} + +Shader* RendererOpenGL::getShaderProgram(unsigned int shaderID) +{ + unsigned int index {0}; + + // Find the index in mShaderProgramVector by counting the number + // of shifts required to reach 0. + while (shaderID > 0) { + shaderID = shaderID >> 1; + ++index; } - inline GLenum convertTextureType(const Texture::Type type) - { - // clang-format off - switch (type) { - case Texture::RGBA: { return GL_RGBA; } break; -#if defined(USE_OPENGLES) - case Texture::BGRA: { return GL_BGRA_EXT; } break; -#else - case Texture::BGRA: { return GL_BGRA; } break; -#endif - case Texture::RED: { return GL_RED; } break; - default: { return GL_ZERO; } - } - // clang-format on - } + if (mShaderProgramVector.size() > index - 1) + return mShaderProgramVector[index - 1]; + else + return nullptr; +} - void setupWindow() - { +bool RendererOpenGL::loadShaders() +{ + LOG(LogInfo) << "Loading shaders..."; -#if defined(USE_OPENGLES) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); -#else - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); -#endif + std::vector shaderFiles; + shaderFiles.push_back(":/shaders/glsl/core.glsl"); + shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl"); + shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl"); + shaderFiles.push_back(":/shaders/glsl/scanlines.glsl"); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - } + for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); ++it) { + Shader* loadShader = new Shader(); - bool createContext() - { - sdlContext = SDL_GL_CreateContext(getSDLWindow()); + loadShader->loadShaderFile(*it, GL_VERTEX_SHADER); + loadShader->loadShaderFile(*it, GL_FRAGMENT_SHADER); - if (!sdlContext) { - LOG(LogError) << "Error creating OpenGL context. " << SDL_GetError(); + if (!loadShader->createProgram()) { + LOG(LogError) << "Could not create shader program."; return false; } -#if defined(_WIN64) - glewInit(); + mShaderProgramVector.push_back(loadShader); + } + + return true; +} + +GLenum RendererOpenGL::convertBlendFactor(const BlendFactor BlendFactorFactor) +{ + // clang-format off + switch (BlendFactorFactor) { + case BlendFactor::ZERO: { return GL_ZERO; } break; + case BlendFactor::ONE: { return GL_ONE; } break; + case BlendFactor::SRC_COLOR: { return GL_SRC_COLOR; } break; + case BlendFactor::ONE_MINUS_SRC_COLOR: { return GL_ONE_MINUS_SRC_COLOR; } break; + case BlendFactor::SRC_ALPHA: { return GL_SRC_ALPHA; } break; + case BlendFactor::ONE_MINUS_SRC_ALPHA: { return GL_ONE_MINUS_SRC_ALPHA; } break; + case BlendFactor::DST_COLOR: { return GL_DST_COLOR; } break; + case BlendFactor::ONE_MINUS_DST_COLOR: { return GL_ONE_MINUS_DST_COLOR; } break; + case BlendFactor::DST_ALPHA: { return GL_DST_ALPHA; } break; + case BlendFactor::ONE_MINUS_DST_ALPHA: { return GL_ONE_MINUS_DST_ALPHA; } break; + default: { return GL_ZERO; } + } + // clang-format on +} + +GLenum RendererOpenGL::convertTextureType(const TextureType type) +{ + // clang-format off + switch (type) { + case TextureType::RGBA: { return GL_RGBA; } break; +#if defined(USE_OPENGLES) + case TextureType::BGRA: { return GL_BGRA_EXT; } break; +#else + case TextureType::BGRA: { return GL_BGRA; } break; +#endif + case TextureType::RED: { return GL_RED; } break; + default: { return GL_ZERO; } + } + // clang-format on +} + +void RendererOpenGL::setup() +{ +#if defined(USE_OPENGLES) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); #endif - SDL_GL_MakeCurrent(getSDLWindow(), sdlContext); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); +} - std::string vendor = - glGetString(GL_VENDOR) ? reinterpret_cast(glGetString(GL_VENDOR)) : ""; - std::string renderer = - glGetString(GL_RENDERER) ? reinterpret_cast(glGetString(GL_RENDERER)) : ""; - std::string version = - glGetString(GL_VERSION) ? reinterpret_cast(glGetString(GL_VERSION)) : ""; - std::string extensions = glGetString(GL_EXTENSIONS) ? - reinterpret_cast(glGetString(GL_EXTENSIONS)) : - ""; +bool RendererOpenGL::createContext() +{ + mSDLContext = SDL_GL_CreateContext(getSDLWindow()); + + if (!mSDLContext) { + LOG(LogError) << "Error creating OpenGL context. " << SDL_GetError(); + return false; + } - LOG(LogInfo) << "GL vendor: " << vendor; - LOG(LogInfo) << "GL renderer: " << renderer; - LOG(LogInfo) << "GL version: " << version; #if defined(_WIN64) - LOG(LogInfo) << "EmulationStation renderer: OpenGL 3.3 with GLEW"; + glewInit(); +#endif + + SDL_GL_MakeCurrent(getSDLWindow(), mSDLContext); + + std::string vendor { + glGetString(GL_VENDOR) ? reinterpret_cast(glGetString(GL_VENDOR)) : ""}; + std::string renderer { + glGetString(GL_RENDERER) ? reinterpret_cast(glGetString(GL_RENDERER)) : ""}; + std::string version { + glGetString(GL_VERSION) ? reinterpret_cast(glGetString(GL_VERSION)) : ""}; + + LOG(LogInfo) << "GL vendor: " << vendor; + LOG(LogInfo) << "GL renderer: " << renderer; + LOG(LogInfo) << "GL version: " << version; +#if defined(_WIN64) + LOG(LogInfo) << "EmulationStation renderer: OpenGL 3.3 with GLEW"; #else #if defined(USE_OPENGLES) - LOG(LogInfo) << "EmulationStation renderer: OpenGL ES 3.0"; + LOG(LogInfo) << "EmulationStation renderer: OpenGL ES 3.0"; #else - LOG(LogInfo) << "EmulationStation renderer: OpenGL 3.3"; + LOG(LogInfo) << "EmulationStation renderer: OpenGL 3.3"; #endif #endif #if !defined(USE_OPENGLES) - // TODO: Fix the issue that causes the first glClearColor function call to fail. - GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + // TODO: Fix the issue that causes the first glClearColor function call to fail. + GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); #endif - GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); - GL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); - GL_CHECK_ERROR(glEnable(GL_BLEND)); - GL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, 1)); - GL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + GL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); + GL_CHECK_ERROR(glEnable(GL_BLEND)); + GL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, 1)); + GL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - // These are used for the shader post processing. - GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO1)); - GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO2)); + // These are used for the shader post processing. + GL_CHECK_ERROR(glGenFramebuffers(1, &mShaderFBO1)); + GL_CHECK_ERROR(glGenFramebuffers(1, &mShaderFBO2)); - GL_CHECK_ERROR(glGenBuffers(1, &vertexBuffer1)); - GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer1)); - GL_CHECK_ERROR(glGenVertexArrays(1, &vertexBuffer2)); - GL_CHECK_ERROR(glBindVertexArray(vertexBuffer2)); + GL_CHECK_ERROR(glGenBuffers(1, &mVertexBuffer1)); + GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer1)); + GL_CHECK_ERROR(glGenVertexArrays(1, &mVertexBuffer2)); + GL_CHECK_ERROR(glBindVertexArray(mVertexBuffer2)); - uint8_t data[4] = {255, 255, 255, 255}; - whiteTexture = createTexture(Texture::RGBA, false, false, true, 1, 1, data); + uint8_t data[4] = {255, 255, 255, 255}; + mWhiteTexture = createTexture(TextureType::RGBA, false, false, true, 1, 1, data); - postProcTexture1 = createTexture(Texture::RGBA, false, false, false, - static_cast(getScreenWidth()), - static_cast(getScreenHeight()), nullptr); + mPostProcTexture1 = createTexture(TextureType::RGBA, false, false, false, + static_cast(getScreenWidth()), + static_cast(getScreenHeight()), nullptr); - postProcTexture2 = createTexture(Texture::RGBA, false, false, false, - static_cast(getScreenWidth()), - static_cast(getScreenHeight()), nullptr); + mPostProcTexture2 = createTexture(TextureType::RGBA, false, false, false, + static_cast(getScreenWidth()), + static_cast(getScreenHeight()), nullptr); - // Attach textures to the shader framebuffers. - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO1)); - GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - postProcTexture1, 0)); + // Attach textures to the shader framebuffers. + GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1)); + GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + mPostProcTexture1, 0)); - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO2)); - GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - postProcTexture2, 0)); + GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO2)); + GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + mPostProcTexture2, 0)); - GL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + GL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); - return true; + return true; +} + +void RendererOpenGL::destroyContext() +{ + GL_CHECK_ERROR(glDeleteFramebuffers(1, &mShaderFBO1)); + GL_CHECK_ERROR(glDeleteFramebuffers(1, &mShaderFBO2)); + destroyTexture(mPostProcTexture1); + destroyTexture(mPostProcTexture2); + destroyTexture(mWhiteTexture); + SDL_GL_DeleteContext(mSDLContext); + mSDLContext = nullptr; +} + +void RendererOpenGL::setMatrix(const glm::mat4& matrix) +{ + mTrans = matrix; + mTrans[3] = glm::round(mTrans[3]); + mTrans = getProjectionMatrix() * mTrans; +} + +void RendererOpenGL::setScissor(const Rect& scissor) +{ + if ((scissor.x == 0) && (scissor.y == 0) && (scissor.w == 0) && (scissor.h == 0)) { + GL_CHECK_ERROR(glDisable(GL_SCISSOR_TEST)); } - - void destroyContext() - { - GL_CHECK_ERROR(glDeleteFramebuffers(1, &shaderFBO1)); - GL_CHECK_ERROR(glDeleteFramebuffers(1, &shaderFBO2)); - destroyTexture(postProcTexture1); - destroyTexture(postProcTexture2); - destroyTexture(whiteTexture); - SDL_GL_DeleteContext(sdlContext); - sdlContext = nullptr; + else { + // glScissor starts at the bottom left of the window. + GL_CHECK_ERROR(glScissor(scissor.x, + static_cast(getWindowHeight()) - scissor.y - scissor.h, + scissor.w, scissor.h)); + GL_CHECK_ERROR(glEnable(GL_SCISSOR_TEST)); } +} - unsigned int createTexture(const Texture::Type type, - const bool linearMinify, - const bool linearMagnify, - const bool repeat, - const unsigned int width, - const unsigned int height, - void* data) - { - const GLenum textureType = convertTextureType(type); - unsigned int texture; - - GL_CHECK_ERROR(glGenTextures(1, &texture)); - GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); - - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - repeat ? static_cast(GL_REPEAT) : - static_cast(GL_CLAMP_TO_EDGE))); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, - repeat ? static_cast(GL_REPEAT) : - static_cast(GL_CLAMP_TO_EDGE))); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - linearMinify ? static_cast(GL_LINEAR) : - static_cast(GL_NEAREST))); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - linearMagnify ? static_cast(GL_LINEAR) : - static_cast(GL_NEAREST))); - - GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType, - GL_UNSIGNED_BYTE, data)); - return texture; - } - - void destroyTexture(const unsigned int texture) - { - GL_CHECK_ERROR(glDeleteTextures(1, &texture)); - } - - void updateTexture(const unsigned int texture, - const Texture::Type type, - const unsigned int x, - const unsigned int y, - const unsigned int width, - const unsigned int height, - void* data) - { - const GLenum textureType = convertTextureType(type); - - GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); - GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, textureType, - GL_UNSIGNED_BYTE, data)); - - GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture)); - } - - void bindTexture(const unsigned int texture) - { - if (texture == 0) - GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture)); - else - GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); - } - - void drawTriangleStrips(const Vertex* vertices, - const unsigned int numVertices, - const Blend::Factor srcBlendFactor, - const Blend::Factor dstBlendFactor) - { - const float width {vertices[3].position[0]}; - const float height {vertices[3].position[1]}; - - GL_CHECK_ERROR( - glBlendFunc(convertBlendFactor(srcBlendFactor), convertBlendFactor(dstBlendFactor))); - - if (vertices->shaders == 0 || vertices->shaders & SHADER_CORE) { - Shader* shader {getShaderProgram(SHADER_CORE)}; - if (shader) { - shader->activateShaders(); - shader->setModelViewProjectionMatrix(mTrans); - shader->setAttribPointers(); - GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, - GL_DYNAMIC_DRAW)); - shader->setOpacity(vertices->opacity); - shader->setSaturation(vertices->saturation); - shader->setDimming(vertices->dimming); - shader->setBGRAToRGBA(vertices->convertBGRAToRGBA); - shader->setFont(vertices->font); - shader->setPostProcessing(vertices->postProcessing); - GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); - shader->deactivateShaders(); - } - } - else if (vertices->shaders & SHADER_BLUR_HORIZONTAL) { - Shader* shader {getShaderProgram(SHADER_BLUR_HORIZONTAL)}; - if (shader) { - shader->activateShaders(); - shader->setModelViewProjectionMatrix(mTrans); - shader->setAttribPointers(); - GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, - GL_DYNAMIC_DRAW)); - shader->setTextureSize({width, height}); - GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); - shader->deactivateShaders(); - } - return; - } - else if (vertices->shaders & SHADER_BLUR_VERTICAL) { - Shader* shader {getShaderProgram(SHADER_BLUR_VERTICAL)}; - if (shader) { - shader->activateShaders(); - shader->setModelViewProjectionMatrix(mTrans); - shader->setAttribPointers(); - GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, - GL_DYNAMIC_DRAW)); - shader->setTextureSize({width, height}); - GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); - shader->deactivateShaders(); - } - return; - } - else if (vertices->shaders & SHADER_SCANLINES) { - Shader* shader {getShaderProgram(SHADER_SCANLINES)}; - float shaderWidth {width * 1.2f}; - // Scale the scanlines relative to screen resolution. - float screenHeightModifier {getScreenHeightModifier()}; - float relativeHeight {height / getScreenHeight()}; - float shaderHeight {0.0f}; - if (relativeHeight == 1.0f) { - // Full screen. - float modifier {1.30f - (0.1f * screenHeightModifier)}; - shaderHeight = height * modifier; - } - else { - // Portion of screen, e.g. gamelist view. - // Average the relative width and height to avoid applying exaggerated - // 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)}; - shaderHeight = height * modifier; - } - if (shader) { - shader->activateShaders(); - shader->setModelViewProjectionMatrix(mTrans); - shader->setAttribPointers(); - GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, - GL_DYNAMIC_DRAW)); - shader->setOpacity(vertices->opacity); - shader->setTextureSize({shaderWidth, shaderHeight}); - GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); - shader->deactivateShaders(); - } - } - } - - void setMatrix(const glm::mat4& matrix) - { - mTrans = matrix; - mTrans[3] = glm::round(mTrans[3]); - mTrans = getProjectionMatrix() * mTrans; - } - - void setScissor(const Rect& scissor) - { - if ((scissor.x == 0) && (scissor.y == 0) && (scissor.w == 0) && (scissor.h == 0)) { - GL_CHECK_ERROR(glDisable(GL_SCISSOR_TEST)); +void RendererOpenGL::setSwapInterval() +{ + if (Settings::getInstance()->getBool("VSync")) { + // Adaptive VSync seems to be nonfunctional or having issues on some hardware + // and drivers, so only attempt to apply regular VSync. + if (SDL_GL_SetSwapInterval(1) == 0) { + LOG(LogInfo) << "Enabling VSync..."; } else { - // glScissor starts at the bottom left of the window. - GL_CHECK_ERROR(glScissor(scissor.x, - static_cast(getWindowHeight()) - scissor.y - scissor.h, - scissor.w, scissor.h)); - GL_CHECK_ERROR(glEnable(GL_SCISSOR_TEST)); + LOG(LogWarning) << "Could not enable VSync: " << SDL_GetError(); } } - - void setSwapInterval() - { - if (Settings::getInstance()->getBool("VSync")) { - // Adaptive VSync seems to be nonfunctional or having issues on some hardware - // and drivers, so only attempt to apply regular VSync. - if (SDL_GL_SetSwapInterval(1) == 0) { - LOG(LogInfo) << "Enabling VSync..."; - } - else { - LOG(LogWarning) << "Could not enable VSync: " << SDL_GetError(); - } - } - else { - SDL_GL_SetSwapInterval(0); - LOG(LogInfo) << "Disabling VSync..."; - } + else { + SDL_GL_SetSwapInterval(0); + LOG(LogInfo) << "Disabling VSync..."; } +} - void swapBuffers() - { +void RendererOpenGL::swapBuffers() +{ #if defined(__APPLE__) - // On macOS when running in the background, the OpenGL driver apparently does not swap - // the frames which leads to a very fast swap time. This makes ES-DE use a lot of CPU - // resources which slows down the games significantly on slower machines. By introducing - // a delay if the swap time is very low we reduce CPU usage while still keeping the - // application functioning normally. - const auto beforeSwap = std::chrono::system_clock::now(); - SDL_GL_SwapWindow(getSDLWindow()); + // On macOS when running in the background, the OpenGL driver apparently does not swap + // the frames which leads to a very fast swap time. This makes ES-DE use a lot of CPU + // resources which slows down the games significantly on slower machines. By introducing + // a delay if the swap time is very low we reduce CPU usage while still keeping the + // application functioning normally. + const auto beforeSwap = std::chrono::system_clock::now(); + SDL_GL_SwapWindow(getSDLWindow()); - if (std::chrono::duration_cast(std::chrono::system_clock::now() - - beforeSwap) - .count() < 3.0) - SDL_Delay(10); + if (std::chrono::duration_cast(std::chrono::system_clock::now() - + beforeSwap) + .count() < 3.0) + SDL_Delay(10); #else - SDL_GL_SwapWindow(getSDLWindow()); + SDL_GL_SwapWindow(getSDLWindow()); #endif - GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); +} + +unsigned int RendererOpenGL::createTexture(const TextureType type, + const bool linearMinify, + const bool linearMagnify, + const bool repeat, + const unsigned int width, + const unsigned int height, + void* data) +{ + const GLenum textureType {RendererOpenGL::convertTextureType(type)}; + unsigned int texture; + + GL_CHECK_ERROR(glGenTextures(1, &texture)); + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); + + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + repeat ? static_cast(GL_REPEAT) : + static_cast(GL_CLAMP_TO_EDGE))); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + repeat ? static_cast(GL_REPEAT) : + static_cast(GL_CLAMP_TO_EDGE))); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + linearMinify ? static_cast(GL_LINEAR) : + static_cast(GL_NEAREST))); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + linearMagnify ? static_cast(GL_LINEAR) : + static_cast(GL_NEAREST))); + + GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType, + GL_UNSIGNED_BYTE, data)); + return texture; +} + +void RendererOpenGL::destroyTexture(const unsigned int texture) +{ + GL_CHECK_ERROR(glDeleteTextures(1, &texture)); +} + +void RendererOpenGL::updateTexture(const unsigned int texture, + const TextureType type, + const unsigned int x, + const unsigned int y, + const unsigned int width, + const unsigned int height, + void* data) +{ + const GLenum textureType {RendererOpenGL::convertTextureType(type)}; + + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); + GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, textureType, + GL_UNSIGNED_BYTE, data)); + + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, mWhiteTexture)); +} + +void RendererOpenGL::bindTexture(const unsigned int texture) +{ + if (texture == 0) + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, mWhiteTexture)); + else + GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); +} + +void RendererOpenGL::drawTriangleStrips(const Vertex* vertices, + const unsigned int numVertices, + const BlendFactor srcBlendFactorFactor, + const BlendFactor dstBlendFactorFactor) +{ + const float width {vertices[3].position[0]}; + const float height {vertices[3].position[1]}; + + GL_CHECK_ERROR(glBlendFunc(RendererOpenGL::convertBlendFactor(srcBlendFactorFactor), + RendererOpenGL::convertBlendFactor(dstBlendFactorFactor))); + + if (vertices->shaders == 0 || vertices->shaders & SHADER_CORE) { + if (mCoreShader == nullptr) + mCoreShader = RendererOpenGL::getShaderProgram(SHADER_CORE); + if (mCoreShader) { + if (mLastShader != mCoreShader) + mCoreShader->activateShaders(); + mCoreShader->setModelViewProjectionMatrix(mTrans); + if (mLastShader != mCoreShader) + mCoreShader->setAttribPointers(); + GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, + GL_DYNAMIC_DRAW)); + mCoreShader->setOpacity(vertices->opacity); + mCoreShader->setSaturation(vertices->saturation); + mCoreShader->setDimming(vertices->dimming); + mCoreShader->setBGRAToRGBA(vertices->convertBGRAToRGBA); + mCoreShader->setFont(vertices->font); + mCoreShader->setPostProcessing(vertices->postProcessing); + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); + mLastShader = mCoreShader; + } + } + else if (vertices->shaders & SHADER_BLUR_HORIZONTAL) { + if (mBlurHorizontalShader == nullptr) + mBlurHorizontalShader = RendererOpenGL::getShaderProgram(SHADER_BLUR_HORIZONTAL); + if (mBlurHorizontalShader) { + if (mLastShader != mBlurHorizontalShader) + mBlurHorizontalShader->activateShaders(); + mBlurHorizontalShader->setModelViewProjectionMatrix(mTrans); + if (mLastShader != mBlurHorizontalShader) + mBlurHorizontalShader->setAttribPointers(); + GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, + GL_DYNAMIC_DRAW)); + mBlurHorizontalShader->setTextureSize({width, height}); + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); + mLastShader = mBlurHorizontalShader; + } + return; + } + else if (vertices->shaders & SHADER_BLUR_VERTICAL) { + if (mBlurVerticalShader == nullptr) + mBlurVerticalShader = RendererOpenGL::getShaderProgram(SHADER_BLUR_VERTICAL); + if (mBlurVerticalShader) { + if (mLastShader != mBlurVerticalShader) + mBlurVerticalShader->activateShaders(); + mBlurVerticalShader->setModelViewProjectionMatrix(mTrans); + if (mLastShader != mBlurVerticalShader) + mBlurVerticalShader->setAttribPointers(); + GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, + GL_DYNAMIC_DRAW)); + mBlurVerticalShader->setTextureSize({width, height}); + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); + mLastShader = mBlurVerticalShader; + } + return; + } + else if (vertices->shaders & SHADER_SCANLINES) { + if (mScanlinelShader == nullptr) + mScanlinelShader = RendererOpenGL::getShaderProgram(SHADER_SCANLINES); + float shaderWidth {width * 1.2f}; + // Scale the scanlines relative to screen resolution. + float screenHeightModifier {getScreenHeightModifier()}; + float relativeHeight {height / getScreenHeight()}; + float shaderHeight {0.0f}; + if (relativeHeight == 1.0f) { + // Full screen. + float modifier {1.30f - (0.1f * screenHeightModifier)}; + shaderHeight = height * modifier; + } + else { + // Portion of screen, e.g. gamelist view. + // Average the relative width and height to avoid applying exaggerated + // 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)}; + shaderHeight = height * modifier; + } + if (mScanlinelShader) { + if (mLastShader != mScanlinelShader) + mScanlinelShader->activateShaders(); + mScanlinelShader->setModelViewProjectionMatrix(mTrans); + if (mLastShader != mScanlinelShader) + mScanlinelShader->setAttribPointers(); + GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, + GL_DYNAMIC_DRAW)); + mScanlinelShader->setOpacity(vertices->opacity); + mScanlinelShader->setTextureSize({shaderWidth, shaderHeight}); + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); + mLastShader = mScanlinelShader; + } + } +} + +void RendererOpenGL::shaderPostprocessing(unsigned int shaders, + const Renderer::postProcessingParams& parameters, + unsigned char* textureRGBA) +{ + Vertex vertices[4]; + std::vector shaderList; + float widthf {getScreenWidth()}; + float heightf {getScreenHeight()}; + GLuint width {static_cast(widthf)}; + GLuint height {static_cast(heightf)}; + + // Set vertex positions and texture coordinates to full screen as all + // post-processing is applied to the complete screen area. + // clang-format off + vertices[0] = {{0.0f, 0.0f }, {0.0f, 1.0f}, 0xFFFFFFFF}; + vertices[1] = {{0.0f, heightf}, {0.0f, 0.0f}, 0xFFFFFFFF}; + vertices[2] = {{widthf, 0.0f }, {1.0f, 1.0f}, 0xFFFFFFFF}; + vertices[3] = {{widthf, heightf}, {1.0f, 0.0f}, 0xFFFFFFFF}; + // clang-format on + + vertices->opacity = parameters.opacity; + vertices->saturation = parameters.saturation; + vertices->dimming = parameters.dimming; + vertices->postProcessing = true; + + if (shaders & SHADER_CORE) + shaderList.push_back(SHADER_CORE); + if (shaders & SHADER_BLUR_HORIZONTAL) + shaderList.push_back(SHADER_BLUR_HORIZONTAL); + if (shaders & SHADER_BLUR_VERTICAL) + shaderList.push_back(SHADER_BLUR_VERTICAL); + if (shaders & SHADER_SCANLINES) + shaderList.push_back(SHADER_SCANLINES); + + setMatrix(getIdentity()); + bindTexture(mPostProcTexture1); + + 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)); + + GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO2)); + + bool firstFBO {true}; + int drawCalls {0}; + + 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 + // to execute, which proportionally affects the blur amount. + if (shaderList[i] == Renderer::SHADER_BLUR_HORIZONTAL || + shaderList[i] == Renderer::SHADER_BLUR_VERTICAL) { + 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. + 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)); + GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1)); + GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT)); + firstFBO = false; + } + else { + bindTexture(mPostProcTexture1); + GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO1)); + GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO2)); + GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT)); + firstFBO = true; + } + } } - void shaderPostprocessing(unsigned int shaders, - const Renderer::postProcessingParams& parameters, - unsigned char* textureRGBA) - { - Vertex vertices[4]; - std::vector shaderList; - float widthf {getScreenWidth()}; - float heightf {getScreenHeight()}; - GLuint width {static_cast(widthf)}; - GLuint height {static_cast(heightf)}; + // If textureRGBA has an address, it means that the output should go to this + // texture rather than to the screen. The glReadPixels() function is slow, but + // since this will typically only run every now and then to create a cached + // screen texture, it doesn't really matter. + if (textureRGBA) { + if (firstFBO) + GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO1)); + else + GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO2)); - // Set vertex positions and texture coordinates to full screen as all - // post-processing is applied to the complete screen area. - // clang-format off - vertices[0] = {{0.0f, 0.0f }, {0.0f, 1.0f}, 0xFFFFFFFF}; - vertices[1] = {{0.0f, heightf}, {0.0f, 0.0f}, 0xFFFFFFFF}; - vertices[2] = {{widthf, 0.0f }, {1.0f, 1.0f}, 0xFFFFFFFF}; - vertices[3] = {{widthf, heightf}, {1.0f, 0.0f}, 0xFFFFFFFF}; - // clang-format on - - vertices->opacity = parameters.opacity; - vertices->saturation = parameters.saturation; - vertices->dimming = parameters.dimming; - vertices->postProcessing = true; - - if (shaders & Renderer::SHADER_CORE) - shaderList.push_back(Renderer::SHADER_CORE); - if (shaders & Renderer::SHADER_BLUR_HORIZONTAL) - shaderList.push_back(Renderer::SHADER_BLUR_HORIZONTAL); - if (shaders & Renderer::SHADER_BLUR_VERTICAL) - shaderList.push_back(Renderer::SHADER_BLUR_VERTICAL); - if (shaders & Renderer::SHADER_SCANLINES) - shaderList.push_back(Renderer::SHADER_SCANLINES); + GL_CHECK_ERROR(glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA)); + GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + } + if (getScreenRotated()) setMatrix(getIdentity()); - bindTexture(postProcTexture1); - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO1)); - - // Blit the screen contents to postProcTexture. - GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, - GL_COLOR_BUFFER_BIT, GL_NEAREST)); - - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO2)); - - bool firstFBO {true}; - int drawCalls {0}; - - 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 - // to execute, which proportionally affects the blur amount. - if (shaderList[i] == Renderer::SHADER_BLUR_HORIZONTAL || - shaderList[i] == Renderer::SHADER_BLUR_VERTICAL) { - 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. - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); - drawTriangleStrips(vertices, 4, Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA); - break; - } - // Apply/render the shaders. - drawTriangleStrips(vertices, 4, Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA); - ++drawCalls; - if (firstFBO) { - bindTexture(postProcTexture2); - GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO2)); - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO1)); - GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT)); - firstFBO = false; - } - else { - bindTexture(postProcTexture1); - GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO1)); - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO2)); - GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT)); - firstFBO = true; - } - } - } - - // If textureRGBA has an address, it means that the output should go to this - // texture rather than to the screen. The glReadPixels() function is slow, but - // since this will typically only run every now and then to create a cached - // screen texture, it doesn't really matter. - if (textureRGBA) { - if (firstFBO) - GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO1)); - else - GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO2)); - - GL_CHECK_ERROR( - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA)); - GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); - } - - if (getScreenRotated()) - setMatrix(getIdentity()); - - GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)); - } - -} // namespace Renderer + GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)); +} diff --git a/es-core/src/renderers/Renderer_GL21.h b/es-core/src/renderers/Renderer_GL21.h new file mode 100644 index 000000000..1934b04a5 --- /dev/null +++ b/es-core/src/renderers/Renderer_GL21.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// Renderer_GL21.h +// +// OpenGL / OpenGL ES renderering functions. +// + +#ifndef ES_CORE_RENDERER_RENDERER_GL21_H +#define ES_CORE_RENDERER_RENDERER_GL21_H + +#include "Shader_GL21.h" +#include "renderers/Renderer.h" + +#if defined(USE_OPENGLES) +#include +#include +#else +#include +#include +#endif + +class RendererOpenGL : public Renderer +{ +public: + RendererOpenGL() noexcept; + ~RendererOpenGL(); + + static RendererOpenGL* getInstance(); + + Shader* getShaderProgram(unsigned int shaderID); + bool loadShaders() override; + + GLenum convertBlendFactor(const BlendFactor BlendFactorFactor); + GLenum convertTextureType(const TextureType type); + + void setup() override; + bool createContext() override; + void destroyContext() override; + + void setMatrix(const glm::mat4& matrix) override; + void setScissor(const Rect& scissor) override; + void setSwapInterval() override; + void swapBuffers() override; + + unsigned int createTexture(const TextureType type, + const bool linearMinify, + const bool linearMagnify, + const bool repeat, + const unsigned int width, + const unsigned int height, + void* data) override; + void destroyTexture(const unsigned int texture) override; + void updateTexture(const unsigned int texture, + const TextureType type, + const unsigned int x, + const unsigned int y, + const unsigned int width, + const unsigned int height, + void* data) override; + void bindTexture(const unsigned int texture) override; + + void drawTriangleStrips( + const Vertex* vertices, + const unsigned int numVertices, + const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA, + const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA) override; + + void shaderPostprocessing( + const unsigned int shaders, + const Renderer::postProcessingParams& parameters = postProcessingParams(), + unsigned char* textureRGBA = nullptr) override; + +private: + std::vector mShaderProgramVector; + GLuint mShaderFBO1; + GLuint mShaderFBO2; + GLuint mVertexBuffer1; + GLuint mVertexBuffer2; + + SDL_GLContext mSDLContext; + GLuint mWhiteTexture; + GLuint mPostProcTexture1; + GLuint mPostProcTexture2; + Shader* mCoreShader; + Shader* mBlurHorizontalShader; + Shader* mBlurVerticalShader; + Shader* mScanlinelShader; + Shader* mLastShader; +}; + +#endif // ES_CORE_RENDERER_RENDERER_GL21_H diff --git a/es-core/src/renderers/Shader_GL21.cpp b/es-core/src/renderers/Shader_GL21.cpp index 9701e08c0..a1a052946 100644 --- a/es-core/src/renderers/Shader_GL21.cpp +++ b/es-core/src/renderers/Shader_GL21.cpp @@ -12,254 +12,246 @@ #include "renderers/Renderer.h" #include "resources/ResourceManager.h" -namespace Renderer +Shader::Shader() + : mProgramID {0} + , mShaderMVPMatrix {0} + , mShaderPosition {0} + , mShaderTextureCoord {0} + , mShaderColor {0} + , mShaderTextureSize {0} + , mShaderOpacity {0} + , mShaderSaturation {0} + , mShaderDimming {0} + , mShaderBGRAToRGBA {0} + , mShaderFont {0} + , mShaderPostProcessing {0} { - Renderer::Shader::Shader() - : mProgramID {0} - , shaderMVPMatrix {0} - , shaderPosition {0} - , shaderTextureCoord {0} - , shaderColor {0} - , shaderTextureSize {0} - , shaderOpacity {0} - , shaderSaturation {0} - , shaderDimming {0} - , shaderBGRAToRGBA {0} - , shaderFont {0} - , shaderPostProcessing {0} - { - } +} - Renderer::Shader::~Shader() - { - // Delete the shader program when destroyed. - deleteProgram(mProgramID); - } +Shader::~Shader() +{ + // Delete the shader program when destroyed. + deleteProgram(mProgramID); +} - void Renderer::Shader::loadShaderFile(const std::string& path, GLenum shaderType) - { - std::string preprocessorDefines; - std::string shaderCode; +void Shader::loadShaderFile(const std::string& path, GLenum shaderType) +{ + std::string preprocessorDefines; + std::string shaderCode; - // This will load the entire GLSL source code into the string variable. - const ResourceData& shaderData {ResourceManager::getInstance().getFileData(path)}; - shaderCode.assign(reinterpret_cast(shaderData.ptr.get()), shaderData.length); + // This will load the entire GLSL source code into the string variable. + const ResourceData& shaderData {ResourceManager::getInstance().getFileData(path)}; + shaderCode.assign(reinterpret_cast(shaderData.ptr.get()), shaderData.length); - // Define the GLSL version. + // Define the GLSL version. #if defined(USE_OPENGLES) - preprocessorDefines = "#version 300 es\n"; + preprocessorDefines = "#version 300 es\n"; #else - preprocessorDefines = "#version 330\n"; + preprocessorDefines = "#version 330\n"; #endif - // Define the preprocessor constants that will let the shader compiler know whether - // the VERTEX or FRAGMENT portion of the code should be used. - if (shaderType == GL_VERTEX_SHADER) - preprocessorDefines += "#define VERTEX\n"; - else if (shaderType == GL_FRAGMENT_SHADER) - preprocessorDefines += "#define FRAGMENT\n"; + // Define the preprocessor constants that will let the shader compiler know whether + // the VERTEX or FRAGMENT portion of the code should be used. + if (shaderType == GL_VERTEX_SHADER) + preprocessorDefines += "#define VERTEX\n"; + else if (shaderType == GL_FRAGMENT_SHADER) + preprocessorDefines += "#define FRAGMENT\n"; - shaderVector.push_back(std::make_tuple(path, preprocessorDefines + shaderCode, shaderType)); - } + mShaderVector.push_back(std::make_tuple(path, preprocessorDefines + shaderCode, shaderType)); +} - bool Renderer::Shader::createProgram() - { - GLint programSuccess; +bool Shader::createProgram() +{ + GLint programSuccess; - mProgramID = glCreateProgram(); + mProgramID = glCreateProgram(); - // Compile and attach all shaders that have been loaded. - for (auto it = shaderVector.cbegin(); it != shaderVector.cend(); ++it) { - GLuint currentShader = glCreateShader(std::get<2>(*it)); - GLchar const* shaderCodePtr = std::get<1>(*it).c_str(); + // Compile and attach all shaders that have been loaded. + for (auto it = mShaderVector.cbegin(); it != mShaderVector.cend(); ++it) { + GLuint currentShader {glCreateShader(std::get<2>(*it))}; + GLchar const* shaderCodePtr {std::get<1>(*it).c_str()}; - glShaderSource(currentShader, 1, reinterpret_cast(&shaderCodePtr), - nullptr); - glCompileShader(currentShader); + glShaderSource(currentShader, 1, reinterpret_cast(&shaderCodePtr), nullptr); + glCompileShader(currentShader); - GLint shaderCompiled; - glGetShaderiv(currentShader, GL_COMPILE_STATUS, &shaderCompiled); + GLint shaderCompiled; + glGetShaderiv(currentShader, GL_COMPILE_STATUS, &shaderCompiled); - if (shaderCompiled != GL_TRUE) { - LOG(LogError) << "OpenGL error: Unable to compile shader " << currentShader << " (" - << std::get<0>(*it) << ")."; - printShaderInfoLog(currentShader, std::get<2>(*it), true); - return false; - } - else { - printShaderInfoLog(currentShader, std::get<2>(*it), false); - } - - GL_CHECK_ERROR(glAttachShader(mProgramID, currentShader)); - } - - glLinkProgram(mProgramID); - - glGetProgramiv(mProgramID, GL_LINK_STATUS, &programSuccess); - if (programSuccess != GL_TRUE) { - LOG(LogError) << "OpenGL error: Unable to link program " << mProgramID << "."; - printProgramInfoLog(mProgramID); + if (shaderCompiled != GL_TRUE) { + LOG(LogError) << "OpenGL error: Unable to compile shader " << currentShader << " (" + << std::get<0>(*it) << ")."; + printShaderInfoLog(currentShader, std::get<2>(*it), true); return false; } - - getVariableLocations(mProgramID); - - if (shaderPosition != -1) - GL_CHECK_ERROR(glEnableVertexAttribArray(shaderPosition)); - - if (shaderTextureCoord != -1) - GL_CHECK_ERROR(glEnableVertexAttribArray(shaderTextureCoord)); - - if (shaderColor != -1) - GL_CHECK_ERROR(glEnableVertexAttribArray(shaderColor)); - - return true; - } - - void Renderer::Shader::deleteProgram(GLuint programID) - { - GL_CHECK_ERROR(glDeleteProgram(programID)); - } - - void Renderer::Shader::getVariableLocations(GLuint programID) - { - // Some of the variable names are chosen to be compatible with the RetroArch GLSL shaders. - shaderMVPMatrix = glGetUniformLocation(mProgramID, "MVPMatrix"); - shaderPosition = glGetAttribLocation(mProgramID, "positionAttrib"); - shaderTextureCoord = glGetAttribLocation(mProgramID, "TexCoord"); - shaderColor = glGetAttribLocation(mProgramID, "colorAttrib"); - shaderTextureSize = glGetUniformLocation(mProgramID, "TextureSize"); - shaderOpacity = glGetUniformLocation(mProgramID, "opacity"); - shaderSaturation = glGetUniformLocation(mProgramID, "saturation"); - shaderDimming = glGetUniformLocation(mProgramID, "dimming"); - shaderBGRAToRGBA = glGetUniformLocation(mProgramID, "BGRAToRGBA"); - shaderFont = glGetUniformLocation(mProgramID, "font"); - shaderPostProcessing = glGetUniformLocation(mProgramID, "postProcessing"); - } - - void Renderer::Shader::setModelViewProjectionMatrix(glm::mat4 mvpMatrix) - { - if (shaderMVPMatrix != GL_INVALID_VALUE && shaderMVPMatrix != GL_INVALID_OPERATION) - GL_CHECK_ERROR(glUniformMatrix4fv(shaderMVPMatrix, 1, GL_FALSE, - reinterpret_cast(&mvpMatrix))); - } - - void Renderer::Shader::setAttribPointers() - { - if (shaderPosition != -1) - GL_CHECK_ERROR( - glVertexAttribPointer(shaderPosition, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, position)))); - if (shaderTextureCoord != -1) - GL_CHECK_ERROR( - glVertexAttribPointer(shaderTextureCoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, texture)))); - - if (shaderColor != -1) - GL_CHECK_ERROR( - glVertexAttribPointer(shaderColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, color)))); - } - - void Renderer::Shader::setTextureSize(std::array shaderVec2) - { - if (shaderTextureSize != -1) - GL_CHECK_ERROR(glUniform2f(shaderTextureSize, shaderVec2[0], shaderVec2[1])); - } - - void Renderer::Shader::setOpacity(GLfloat opacity) - { - if (shaderOpacity != -1) - GL_CHECK_ERROR(glUniform1f(shaderOpacity, opacity)); - } - - void Renderer::Shader::setSaturation(GLfloat saturation) - { - if (shaderSaturation != -1) - GL_CHECK_ERROR(glUniform1f(shaderSaturation, saturation)); - } - - void Renderer::Shader::setDimming(GLfloat dimming) - { - if (shaderDimming != -1) - GL_CHECK_ERROR(glUniform1f(shaderDimming, dimming)); - } - - void Renderer::Shader::setBGRAToRGBA(GLboolean BGRAToRGBA) - { - if (shaderBGRAToRGBA != -1) - GL_CHECK_ERROR(glUniform1i(shaderBGRAToRGBA, BGRAToRGBA ? 1 : 0)); - } - - void Renderer::Shader::setFont(GLboolean font) - { - if (shaderFont != -1) - GL_CHECK_ERROR(glUniform1i(shaderFont, font ? 1 : 0)); - } - - void Renderer::Shader::setPostProcessing(GLboolean postProcessing) - { - if (shaderPostProcessing != -1) - GL_CHECK_ERROR(glUniform1i(shaderPostProcessing, postProcessing ? 1 : 0)); - } - - void Renderer::Shader::activateShaders() - { - // Install the shader program. - GL_CHECK_ERROR(glUseProgram(mProgramID)); - } - - void Renderer::Shader::deactivateShaders() - { - // Remove the shader program. - GL_CHECK_ERROR(glUseProgram(0)); - } - - void Renderer::Shader::printProgramInfoLog(GLuint programID) - { - if (glIsProgram(programID)) { - int logLength; - int maxLength; - - glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxLength); - std::vector infoLog(maxLength); - - glGetProgramInfoLog(programID, maxLength, &logLength, &infoLog.front()); - - if (logLength > 0) { - LOG(LogDebug) << "Renderer_GL21::printProgramInfoLog():\n" - << std::string(infoLog.begin(), infoLog.end()); - } - } else { - LOG(LogError) << "OpenGL error: " << programID << " is not a program."; + printShaderInfoLog(currentShader, std::get<2>(*it), false); } + + GL_CHECK_ERROR(glAttachShader(mProgramID, currentShader)); } - void Renderer::Shader::printShaderInfoLog(GLuint shaderID, GLenum shaderType, bool error) - { - if (glIsShader(shaderID)) { - int logLength; - int maxLength; + glLinkProgram(mProgramID); - glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxLength); - std::vector infoLog(maxLength); - - if (infoLog.size() == 0) - return; - - glGetShaderInfoLog(shaderID, maxLength, &logLength, &infoLog.front()); - - if (logLength > 0) { - LOG(LogDebug) << "Shader_GL21::printShaderInfoLog(): " - << (error ? "Error" : "Warning") << " in " - << (shaderType == GL_VERTEX_SHADER ? "VERTEX section:\n" : - "FRAGMENT section:\n") - << std::string(infoLog.begin(), infoLog.end()); - } - } - else { - LOG(LogError) << "OpenGL error: " << shaderID << " is not a shader."; - } + glGetProgramiv(mProgramID, GL_LINK_STATUS, &programSuccess); + if (programSuccess != GL_TRUE) { + LOG(LogError) << "OpenGL error: Unable to link program " << mProgramID << "."; + printProgramInfoLog(mProgramID); + return false; } -} // namespace Renderer + getVariableLocations(mProgramID); + + if (mShaderPosition != -1) + GL_CHECK_ERROR(glEnableVertexAttribArray(mShaderPosition)); + + if (mShaderTextureCoord != -1) + GL_CHECK_ERROR(glEnableVertexAttribArray(mShaderTextureCoord)); + + if (mShaderColor != -1) + GL_CHECK_ERROR(glEnableVertexAttribArray(mShaderColor)); + + return true; +} + +void Shader::deleteProgram(GLuint programID) { GL_CHECK_ERROR(glDeleteProgram(programID)); } + +void Shader::getVariableLocations(GLuint programID) +{ + // Some of the variable names are chosen to be compatible with the RetroArch GLSL shaders. + mShaderMVPMatrix = glGetUniformLocation(mProgramID, "MVPMatrix"); + mShaderPosition = glGetAttribLocation(mProgramID, "positionAttrib"); + mShaderTextureCoord = glGetAttribLocation(mProgramID, "TexCoord"); + mShaderColor = glGetAttribLocation(mProgramID, "colorAttrib"); + mShaderTextureSize = glGetUniformLocation(mProgramID, "TextureSize"); + mShaderOpacity = glGetUniformLocation(mProgramID, "opacity"); + mShaderSaturation = glGetUniformLocation(mProgramID, "saturation"); + mShaderDimming = glGetUniformLocation(mProgramID, "dimming"); + mShaderBGRAToRGBA = glGetUniformLocation(mProgramID, "BGRAToRGBA"); + mShaderFont = glGetUniformLocation(mProgramID, "font"); + mShaderPostProcessing = glGetUniformLocation(mProgramID, "postProcessing"); +} + +void Shader::setModelViewProjectionMatrix(glm::mat4 mvpMatrix) +{ + if (mShaderMVPMatrix != GL_INVALID_VALUE && mShaderMVPMatrix != GL_INVALID_OPERATION) + GL_CHECK_ERROR(glUniformMatrix4fv(mShaderMVPMatrix, 1, GL_FALSE, + reinterpret_cast(&mvpMatrix))); +} + +void Shader::setAttribPointers() +{ + if (mShaderPosition != -1) + GL_CHECK_ERROR(glVertexAttribPointer( + mShaderPosition, 2, GL_FLOAT, GL_FALSE, sizeof(Renderer::Vertex), + reinterpret_cast(offsetof(Renderer::Vertex, position)))); + if (mShaderTextureCoord != -1) + GL_CHECK_ERROR(glVertexAttribPointer( + mShaderTextureCoord, 2, GL_FLOAT, GL_FALSE, sizeof(Renderer::Vertex), + reinterpret_cast(offsetof(Renderer::Vertex, texture)))); + + if (mShaderColor != -1) + GL_CHECK_ERROR(glVertexAttribPointer( + mShaderColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Renderer::Vertex), + reinterpret_cast(offsetof(Renderer::Vertex, color)))); +} + +void Shader::setTextureSize(std::array shaderVec2) +{ + if (mShaderTextureSize != -1) + GL_CHECK_ERROR(glUniform2f(mShaderTextureSize, shaderVec2[0], shaderVec2[1])); +} + +void Shader::setOpacity(GLfloat opacity) +{ + if (mShaderOpacity != -1) + GL_CHECK_ERROR(glUniform1f(mShaderOpacity, opacity)); +} + +void Shader::setSaturation(GLfloat saturation) +{ + if (mShaderSaturation != -1) + GL_CHECK_ERROR(glUniform1f(mShaderSaturation, saturation)); +} + +void Shader::setDimming(GLfloat dimming) +{ + if (mShaderDimming != -1) + GL_CHECK_ERROR(glUniform1f(mShaderDimming, dimming)); +} + +void Shader::setBGRAToRGBA(GLboolean BGRAToRGBA) +{ + if (mShaderBGRAToRGBA != -1) + GL_CHECK_ERROR(glUniform1i(mShaderBGRAToRGBA, BGRAToRGBA ? 1 : 0)); +} + +void Shader::setFont(GLboolean font) +{ + if (mShaderFont != -1) + GL_CHECK_ERROR(glUniform1i(mShaderFont, font ? 1 : 0)); +} + +void Shader::setPostProcessing(GLboolean postProcessing) +{ + if (mShaderPostProcessing != -1) + GL_CHECK_ERROR(glUniform1i(mShaderPostProcessing, postProcessing ? 1 : 0)); +} + +void Shader::activateShaders() +{ + // Install the shader program. + GL_CHECK_ERROR(glUseProgram(mProgramID)); +} + +void Shader::deactivateShaders() +{ + // Remove the shader program. + GL_CHECK_ERROR(glUseProgram(0)); +} + +void Shader::printProgramInfoLog(GLuint programID) +{ + if (glIsProgram(programID)) { + int logLength; + int maxLength; + + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxLength); + std::vector infoLog(maxLength); + + glGetProgramInfoLog(programID, maxLength, &logLength, &infoLog.front()); + + if (logLength > 0) { + LOG(LogDebug) << "Renderer_GL21::printProgramInfoLog():\n" + << std::string(infoLog.begin(), infoLog.end()); + } + } + else { + LOG(LogError) << "OpenGL error: " << programID << " is not a program."; + } +} + +void Shader::printShaderInfoLog(GLuint shaderID, GLenum shaderType, bool error) +{ + if (glIsShader(shaderID)) { + int logLength; + int maxLength; + + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxLength); + std::vector infoLog(maxLength); + + if (infoLog.size() == 0) + return; + + glGetShaderInfoLog(shaderID, maxLength, &logLength, &infoLog.front()); + + if (logLength > 0) { + LOG(LogDebug) << "Shader_GL21::printShaderInfoLog(): " << (error ? "Error" : "Warning") + << " in " + << (shaderType == GL_VERTEX_SHADER ? "VERTEX section:\n" : + "FRAGMENT section:\n") + << std::string(infoLog.begin(), infoLog.end()); + } + } + else { + LOG(LogError) << "OpenGL error: " << shaderID << " is not a shader."; + } +} diff --git a/es-core/src/renderers/Shader_GL21.h b/es-core/src/renderers/Shader_GL21.h index df26db76e..d5049d6bf 100644 --- a/es-core/src/renderers/Shader_GL21.h +++ b/es-core/src/renderers/Shader_GL21.h @@ -11,6 +11,7 @@ #define GL_GLEXT_PROTOTYPES +#include "renderers/Renderer.h" #include "utils/MathUtil.h" #if defined(_WIN64) @@ -28,61 +29,77 @@ #include #include -namespace Renderer +#if !defined(NDEBUG) +#define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function)) +static inline void _GLCheckError(const std::string& funcName) { - class Shader - { - public: - Shader(); - ~Shader(); + const GLenum errorCode = glGetError(); - // Loads the shader source code only, no compilation done at this point. - void loadShaderFile(const std::string& path, GLenum shaderType); - // Compilation, shader attachment and linking. - bool createProgram(); - // Only used for a clean shutdown. - void deleteProgram(GLuint programID); - // Get references to the variables inside the compiled shaders. - void getVariableLocations(GLuint programID); - // One-way communication with the compiled shaders. - void setModelViewProjectionMatrix(glm::mat4 mvpMatrix); + if (errorCode != GL_NO_ERROR) { +#if defined(USE_OPENGLES) + LOG(LogError) << "OpenGL ES error: " << funcName << " failed with error code: 0x" + << std::hex << errorCode; +#else + LOG(LogError) << "OpenGL error: " << funcName << " failed with error code: 0x" << std::hex + << errorCode; +#endif + } +} +#else +#define GL_CHECK_ERROR(Function) (Function) +#endif - void setAttribPointers(); - void setTextureSize(std::array shaderVec2); - void setOpacity(GLfloat opacity); - void setSaturation(GLfloat saturation); - void setDimming(GLfloat dimming); - void setBGRAToRGBA(GLboolean BGRAToRGBA); - void setFont(GLboolean font); - void setPostProcessing(GLboolean postProcessing); - // Sets the shader program to use the loaded shaders. - void activateShaders(); - // Sets the shader program to 0 which reverts to the fixed function pipeline. - void deactivateShaders(); - // Returns the program ID that was generated by glCreateProgram(). - GLuint getProgramID() { return mProgramID; } - // Only used for error logging if the shaders fail to compile or link. - void printProgramInfoLog(GLuint programID); - void printShaderInfoLog(GLuint shaderID, GLenum shaderType, bool error); +class Shader +{ +public: + Shader(); + ~Shader(); - private: - GLuint mProgramID; - std::vector> shaderVector; + // Loads the shader source code only, no compilation done at this point. + void loadShaderFile(const std::string& path, GLenum shaderType); + // Compilation, shader attachment and linking. + bool createProgram(); + // Only used for a clean shutdown. + void deleteProgram(GLuint programID); + // Get references to the variables inside the compiled shaders. + void getVariableLocations(GLuint programID); + // One-way communication with the compiled shaders. + void setModelViewProjectionMatrix(glm::mat4 mvpMatrix); - // Variables used for communication with the compiled shaders. - GLint shaderMVPMatrix; - GLint shaderPosition; - GLint shaderTextureCoord; - GLint shaderColor; - GLint shaderTextureSize; - GLint shaderOpacity; - GLint shaderSaturation; - GLint shaderDimming; - GLint shaderBGRAToRGBA; - GLint shaderFont; - GLint shaderPostProcessing; - }; + void setAttribPointers(); + void setTextureSize(std::array shaderVec2); + void setOpacity(GLfloat opacity); + void setSaturation(GLfloat saturation); + void setDimming(GLfloat dimming); + void setBGRAToRGBA(GLboolean BGRAToRGBA); + void setFont(GLboolean font); + void setPostProcessing(GLboolean postProcessing); + // Sets the shader program to use the loaded shaders. + void activateShaders(); + // Sets the shader program to 0 which reverts to the fixed function pipeline. + void deactivateShaders(); + // Returns the program ID that was generated by glCreateProgram(). + GLuint getProgramID() { return mProgramID; } + // Only used for error logging if the shaders fail to compile or link. + void printProgramInfoLog(GLuint programID); + void printShaderInfoLog(GLuint shaderID, GLenum shaderType, bool error); -} // namespace Renderer +private: + GLuint mProgramID; + std::vector> mShaderVector; + + // Variables used for communication with the compiled shaders. + GLint mShaderMVPMatrix; + GLint mShaderPosition; + GLint mShaderTextureCoord; + GLint mShaderColor; + GLint mShaderTextureSize; + GLint mShaderOpacity; + GLint mShaderSaturation; + GLint mShaderDimming; + GLint mShaderBGRAToRGBA; + GLint mShaderFont; + GLint mShaderPostProcessing; +}; #endif // ES_CORE_RENDERER_SHADER_GL21_H diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index 47ef377ec..81599b89d 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -76,7 +76,8 @@ size_t Font::getTotalMemUsage() } Font::Font(int size, const std::string& path) - : mSize(size) + : mRenderer {Renderer::getInstance()} + , mSize(size) , mPath(path) { if (mSize < 9) { @@ -199,14 +200,14 @@ bool Font::FontTexture::findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out void Font::FontTexture::initTexture() { assert(textureId == 0); - textureId = Renderer::createTexture(Renderer::Texture::RED, false, false, false, textureSize.x, - textureSize.y, nullptr); + textureId = Renderer::getInstance()->createTexture( + Renderer::TextureType::RED, false, false, false, textureSize.x, textureSize.y, nullptr); } void Font::FontTexture::deinitTexture() { if (textureId != 0) { - Renderer::destroyTexture(textureId); + Renderer::getInstance()->destroyTexture(textureId); textureId = 0; } } @@ -346,8 +347,8 @@ Font::Glyph* Font::getGlyph(unsigned int id) static_cast(g->metrics.horiBearingY) / 64.0f}; // Upload glyph bitmap to texture. - Renderer::updateTexture(tex->textureId, Renderer::Texture::RED, cursor.x, cursor.y, glyphSize.x, - glyphSize.y, g->bitmap.buffer); + mRenderer->updateTexture(tex->textureId, Renderer::TextureType::RED, cursor.x, cursor.y, + glyphSize.x, glyphSize.y, g->bitmap.buffer); // Update max glyph height. if (glyphSize.y > mMaxGlyphHeight) @@ -380,8 +381,8 @@ void Font::rebuildTextures() static_cast(it->second.texSize.y * tex->textureSize.y)}; // Upload to texture. - Renderer::updateTexture(tex->textureId, Renderer::Texture::RED, cursor.x, cursor.y, - glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer); + mRenderer->updateTexture(tex->textureId, Renderer::TextureType::RED, cursor.x, cursor.y, + glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer); } } @@ -398,9 +399,9 @@ void Font::renderTextCache(TextCache* cache) auto vertexList = *it; it->verts[0].font = true; - Renderer::bindTexture(*it->textureIdPtr); - Renderer::drawTriangleStrips(&it->verts[0], - static_cast(it->verts.size())); + mRenderer->bindTexture(*it->textureIdPtr); + mRenderer->drawTriangleStrips(&it->verts[0], + static_cast(it->verts.size())); } } diff --git a/es-core/src/resources/Font.h b/es-core/src/resources/Font.h index 99190d490..71280c7dc 100644 --- a/es-core/src/resources/Font.h +++ b/es-core/src/resources/Font.h @@ -107,6 +107,7 @@ public: static size_t getTotalMemUsage(); private: + Renderer* mRenderer; static FT_Library sLibrary; static std::map, std::weak_ptr> sFontMap; diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index a07ce4fff..707e6911a 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -24,7 +24,8 @@ #define DPI 96 TextureData::TextureData(bool tile) - : mTile {tile} + : mRenderer {Renderer::getInstance()} + , mTile {tile} , mTextureID {0} , mDataRGBA {} , mWidth {0} @@ -209,7 +210,7 @@ bool TextureData::uploadAndBind() // Check if it has already been uploaded. std::unique_lock lock(mMutex); if (mTextureID != 0) { - Renderer::bindTexture(mTextureID); + mRenderer->bindTexture(mTextureID); } else { // Make sure we're ready to upload. @@ -218,9 +219,9 @@ bool TextureData::uploadAndBind() // Upload texture. mTextureID = - Renderer::createTexture(Renderer::Texture::RGBA, true, mLinearMagnify, mTile, - static_cast(mWidth), - static_cast(mHeight), mDataRGBA.data()); + mRenderer->createTexture(Renderer::TextureType::RGBA, true, mLinearMagnify, mTile, + static_cast(mWidth), + static_cast(mHeight), mDataRGBA.data()); } return true; } @@ -229,7 +230,7 @@ void TextureData::releaseVRAM() { std::unique_lock lock(mMutex); if (mTextureID != 0) { - Renderer::destroyTexture(mTextureID); + mRenderer->destroyTexture(mTextureID); mTextureID = 0; } } diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index 48c38fe36..ed04e5d85 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -72,6 +72,7 @@ public: bool tiled() { return mTile; } private: + Renderer* mRenderer; std::mutex mMutex; bool mTile;