LCOV - code coverage report
Current view: top level - muse/framework/ui - graphicsapiprovider.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 146 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2024 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "graphicsapiprovider.h"
      23              : 
      24              : #include <QFile>
      25              : #include <QQuickWindow>
      26              : #include <QSGRendererInterface>
      27              : 
      28              : #include "global/internal/globalconfiguration.h"
      29              : #include "global/logger.h"
      30              : 
      31              : #include "log.h"
      32              : 
      33              : using namespace muse::ui;
      34              : using namespace muse::logger;
      35              : 
      36              : #ifdef Q_OS_WIN
      37              : static const std::vector<GraphicsApi > ALLOWED_APIS = {
      38              :     GraphicsApi::Direct3D11,
      39              :     GraphicsApi::OpenGL,
      40              :     GraphicsApi::Software
      41              : };
      42              : #endif
      43              : 
      44              : #ifdef Q_OS_MACOS
      45              : static const std::vector<GraphicsApi > ALLOWED_APIS = {
      46              :     GraphicsApi::Metal,
      47              :     GraphicsApi::OpenGL,
      48              :     GraphicsApi::Software
      49              : };
      50              : #endif
      51              : 
      52              : #ifdef Q_OS_LINUX
      53              : static const std::vector<GraphicsApi > ALLOWED_APIS = {
      54              :     GraphicsApi::OpenGL,
      55              :     GraphicsApi::Software
      56              : };
      57              : #endif
      58              : 
      59              : #ifdef Q_OS_FREEBSD
      60              : static const std::vector<GraphicsApi > ALLOWED_APIS = {
      61              :     GraphicsApi::OpenGL,
      62              :     GraphicsApi::Software
      63              : };
      64              : #endif
      65              : 
      66              : #ifdef Q_OS_WASM
      67              : static const std::vector<GraphicsApi > ALLOWED_APIS = {
      68              :     GraphicsApi::OpenGL,
      69              :     GraphicsApi::Software
      70              : };
      71              : #endif
      72              : 
      73              : static const std::vector<std::string> BAD_MESSAGES = {
      74              :     "Failed to build graphics pipeline state",
      75              :     "Failed to create RHI",
      76              :     "Failed to build texture render target for layer",
      77              :     "Framebuffer incomplete:"
      78              : };
      79              : 
      80              : namespace muse::ui {
      81              : class GraphicsProblemsDetectorLogDest : public LogDest
      82              : {
      83              : public:
      84              : 
      85            0 :     GraphicsProblemsDetectorLogDest()
      86            0 :         : LogDest(LogLayout("")) {}
      87              : 
      88            0 :     std::string name() const { return "GraphicsProblemsDetectorLogDest"; }
      89            0 :     void write(const LogMsg& logMsg)
      90              :     {
      91            0 :         if (isProblemDetected) {
      92            0 :             return;
      93              :         }
      94              : 
      95            0 :         for (const std::string& m : BAD_MESSAGES) {
      96            0 :             if (logMsg.message.find(m) != std::string::npos) {
      97            0 :                 isProblemDetected = true;
      98            0 :                 std::cout << "found message: " << m << std::endl;
      99            0 :                 break;
     100              :             }
     101              :         }
     102              :     }
     103              : 
     104              :     bool isProblemDetected = false;
     105              : };
     106              : }
     107              : 
     108            0 : GraphicsApiProvider::GraphicsApiProvider(const Version& appVersion)
     109            0 :     : m_appVersion(appVersion)
     110              : {
     111            0 :     Logger* logger = Logger::instance();
     112              : 
     113            0 :     m_logDest = new GraphicsProblemsDetectorLogDest();
     114            0 :     logger->addDest(m_logDest);
     115              : 
     116            0 :     QObject::connect(&m_timer, &QTimer::timeout, [this]() {
     117              :         // fail case
     118            0 :         if (m_logDest->isProblemDetected) {
     119            0 :             if (m_onResult) {
     120            0 :                 m_onResult(false);
     121              :             }
     122            0 :             m_timer.stop();
     123              :         } else
     124              :         // success case
     125            0 :         if (testObjectIsAlive && testObjectHasPainted) {
     126            0 :             if (m_onResult) {
     127            0 :                 m_onResult(true);
     128              :             }
     129            0 :             m_timer.stop();
     130              :         }
     131            0 :     });
     132            0 : }
     133              : 
     134            0 : GraphicsApiProvider::~GraphicsApiProvider()
     135              : {
     136            0 :     m_timer.stop();
     137            0 :     Logger* logger = Logger::instance();
     138            0 :     logger->removeDest(m_logDest);
     139            0 :     delete m_logDest;
     140            0 : }
     141              : 
     142            0 : void GraphicsApiProvider::setGraphicsApi(GraphicsApi api)
     143              : {
     144            0 :     QQuickWindow::setGraphicsApi(static_cast<QSGRendererInterface::GraphicsApi>(api));
     145            0 : }
     146              : 
     147            0 : GraphicsApi GraphicsApiProvider::graphicsApi()
     148              : {
     149              :     //! NOTE QQuickWindow::graphicsApi returns a not valid value,
     150              :     //! it does not take into account the current backend
     151              : 
     152            0 :     QString backend = QQuickWindow::sceneGraphBackend();
     153            0 :     if (backend == "software") {
     154            0 :         return GraphicsApi::Software;
     155            0 :     } else if (backend == "openvg") {
     156            0 :         return GraphicsApi::OpenVG;
     157              :     }
     158              : 
     159            0 :     return static_cast<GraphicsApi>(QQuickWindow::graphicsApi());
     160            0 : }
     161              : 
     162            0 : QString GraphicsApiProvider::graphicsApiName()
     163              : {
     164            0 :     return apiName(graphicsApi());
     165              : }
     166              : 
     167            0 : GraphicsApi GraphicsApiProvider::apiByName(const QString& name)
     168              : {
     169            0 :     for (const GraphicsApi& a : ALLOWED_APIS) {
     170            0 :         if (apiName(a) == name) {
     171            0 :             return a;
     172              :         }
     173              :     }
     174            0 :     return GraphicsApi::Default;
     175              : }
     176              : 
     177            0 : QString GraphicsApiProvider::apiName(GraphicsApi api)
     178              : {
     179            0 :     switch (api) {
     180            0 :     case GraphicsApi::Default: return "default";
     181            0 :     case GraphicsApi::Software: return "software";
     182            0 :     case GraphicsApi::OpenVG: return "openvg";
     183            0 :     case GraphicsApi::OpenGL: return "opengl";
     184            0 :     case GraphicsApi::Direct3D11: return "d3d11";
     185            0 :     case GraphicsApi::Vulkan: return "vulkan";
     186            0 :     case GraphicsApi::Metal: return "metal";
     187            0 :     case GraphicsApi::Null: return "null";
     188              :     }
     189              : 
     190            0 :     return QString();
     191              : }
     192              : 
     193            0 : QString GraphicsApiProvider::dataFilePath() const
     194              : {
     195            0 :     GlobalConfiguration conf;
     196            0 :     return conf.userAppDataPath().toQString() + "/required_graphicsapi.dat";
     197            0 : }
     198              : 
     199            0 : GraphicsApiProvider::Data GraphicsApiProvider::readData() const
     200              : {
     201            0 :     QFile file(dataFilePath());
     202            0 :     if (!file.exists()) {
     203            0 :         return Data();
     204              :     }
     205              : 
     206            0 :     if (!file.open(QIODevice::ReadOnly)) {
     207            0 :         return Data();
     208              :     }
     209              : 
     210            0 :     QString str = QString::fromUtf8(file.readAll());
     211              : 
     212              :     // format:  appversion|api|status
     213            0 :     QStringList vals = str.split('|');
     214              : 
     215              :     //! NOTE Check format
     216            0 :     if (vals.size() < 3) {
     217            0 :         LOGE() << "Graphics GraphicsApi data has bad format: " << str << ", file: " << file.fileName();
     218            0 :         return Data();
     219              :     }
     220              : 
     221            0 :     Data d;
     222            0 :     d.appVersion = vals.at(0).trimmed();
     223            0 :     d.apiName = vals.at(1).trimmed();
     224            0 :     d.status = static_cast<Status>(vals.at(2).trimmed().toInt());
     225              : 
     226            0 :     return d;
     227            0 : }
     228              : 
     229            0 : void GraphicsApiProvider::writeData(const Data& d)
     230              : {
     231            0 :     QStringList vals = { d.appVersion, d.apiName, QString::number(int(d.status)) };
     232            0 :     QString str = vals.join('|');
     233              : 
     234            0 :     QFile file(dataFilePath());
     235            0 :     if (!file.open(QIODevice::WriteOnly)) {
     236            0 :         LOGE() << "Failed to write Graphics GraphicsApi data to file: " << file.fileName();
     237            0 :         return;
     238              :     }
     239            0 :     file.write(str.toUtf8());
     240            0 : }
     241              : 
     242            0 : GraphicsApi GraphicsApiProvider::requiredGraphicsApi()
     243              : {
     244            0 :     Data data = readData();
     245            0 :     if (!data.isValid()) {
     246            0 :         return GraphicsApi::Default;
     247              :     }
     248              : 
     249              :     //! NOTE For the new version, we will repeat the check starting with the default API
     250            0 :     if (m_appVersion.toString() != data.appVersion) {
     251            0 :         LOGI() << "Graphics GraphicsApi data from another version, the check will be repeated starting from the default API,"
     252            0 :                << " data app version: " << data.appVersion
     253            0 :                << " current app version: " << m_appVersion.toString();
     254            0 :         return GraphicsApi::Default;
     255              :     }
     256              : 
     257            0 :     GraphicsApi api = apiByName(data.apiName);
     258            0 :     IF_ASSERT_FAILED(api != GraphicsApi::Default) {
     259            0 :         return switchToNextGraphicsApi(api);
     260              :     }
     261              : 
     262              :     //! NOTE If there is a required API, but it status is checking,
     263              :     //! then something bad happened with its use (for example, a crash),
     264              :     //! so we switch to the next one.
     265            0 :     if (data.status == Status::Checking) {
     266            0 :         return switchToNextGraphicsApi(api);
     267              :     }
     268              : 
     269            0 :     return api;
     270            0 : }
     271              : 
     272            0 : void GraphicsApiProvider::setGraphicsApiStatus(GraphicsApi api, Status status)
     273              : {
     274            0 :     if (api == GraphicsApi::Default) {
     275            0 :         return;
     276              :     }
     277              : 
     278            0 :     Data d;
     279            0 :     d.appVersion = m_appVersion.toString();
     280            0 :     d.apiName = apiName(api);
     281            0 :     d.status = status;
     282            0 :     writeData(d);
     283            0 : }
     284              : 
     285            0 : GraphicsApi GraphicsApiProvider::nextGraphicsApi(GraphicsApi api) const
     286              : {
     287              :     //! NOTE If this is the default value, then it means the current one, since we shouldn't have changed it yet.
     288            0 :     if (api == GraphicsApi::Default) {
     289            0 :         api = graphicsApi();
     290              :     }
     291              : 
     292            0 :     auto it = std::find(ALLOWED_APIS.begin(), ALLOWED_APIS.end(), api);
     293            0 :     IF_ASSERT_FAILED(it != ALLOWED_APIS.end()) {
     294              :         //! NOTE Something strange happened that shouldn't happen.
     295              :         //! Let's switch to software to avoid an infinite loop in which there is an error
     296            0 :         return GraphicsApi::Software;
     297              :     }
     298              : 
     299            0 :     ++it;
     300              : 
     301              :     //! NOTE This was the last api
     302            0 :     if (it == ALLOWED_APIS.end()) {
     303            0 :         return GraphicsApi::Software;
     304              :     }
     305              : 
     306            0 :     return *it;
     307              : }
     308              : 
     309            0 : GraphicsApi GraphicsApiProvider::switchToNextGraphicsApi(GraphicsApi current)
     310              : {
     311            0 :     GraphicsApi api = nextGraphicsApi(current);
     312              : 
     313            0 :     Data d;
     314            0 :     d.appVersion = m_appVersion.toString();
     315            0 :     d.apiName = apiName(api);
     316            0 :     d.status = Status::NeedCheck;
     317            0 :     writeData(d);
     318              : 
     319            0 :     return api;
     320            0 : }
     321              : 
     322            0 : void GraphicsApiProvider::listen(const OnResult& f)
     323              : {
     324            0 :     m_onResult = f;
     325            0 :     m_timer.start(10);
     326            0 : }
     327              : 
     328            0 : void GraphicsApiProvider::destroy()
     329              : {
     330            0 :     GraphicsApiProvider* self = this;
     331            0 :     QTimer::singleShot(1, [self]() {
     332            0 :         delete self;
     333            0 :     });
     334            0 : }
        

Generated by: LCOV version 2.0-1