/
tomilov
/
sah_kd_tree
Обзор
Документация
Войти
/
tomilov
/
sah_kd_tree
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
src/viewer/main.cpp
380 строк
15 KB
Anatoliy Tomilov
WIP
09 сен 2024, 00:30
09 сен 2024, 00:30
adbc9de
Код
Авторство
О чём код?
#include <common/version.hpp> #include <engine/context.hpp> #include <engine/device.hpp> #include <engine/instance.hpp> #include <engine/physical_device.hpp> #include <format/vulkan.hpp> #include <utils/assert.hpp> #include <utils/auto_cast.hpp> #include <viewer/application.hpp> #include <viewer/engine_wrapper.hpp> #include <spdlog/details/null_mutex.h> #include <spdlog/sinks/base_sink.h> #include <spdlog/spdlog.h> #include <vulkan/vulkan.hpp> #include <QtCore/QByteArrayAlgorithms> #include <QtCore/QCoreApplication> #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QDirIterator> #include <QtCore/QLoggingCategory> #include <QtCore/QObject> #include <QtCore/QSettings> #include <QtCore/QString> #include <QtCore/QTimer> #include <QtCore/QUrl> #include <QtCore/QVersionNumber> #include <QtCore/QtMessageHandler> #include <QtGui/QFont> #include <QtGui/QFontDatabase> #include <QtGui/QGuiApplication> #include <QtGui/QVulkanInstance> #include <QtQml/QQmlApplicationEngine> #include <QtQml/QQmlContext> #include <QtQuick/QQuickGraphicsConfiguration> #include <QtQuick/QQuickGraphicsDevice> #include <QtQuick/QQuickView> #include <QtQuick/QQuickWindow> #include <QtQuick/QSGRendererInterface> #include <QtQuickControls2/QQuickStyle> #include <QtWidgets/QApplication> #include <iterator> #include <memory> #include <optional> #include <utility> #include <cstdlib> using namespace Qt::StringLiterals; namespace { Q_DECLARE_LOGGING_CATEGORY(viewerMainCategory) Q_LOGGING_CATEGORY(viewerMainCategory, "viewer.main") using AppPtr = std::unique_ptr<QGuiApplication>; constexpr bool kUseEngine = true; AppPtr createApplication(int & argc, char * argv[]) { for (int i = 1; i < argc; ++i) { if (qstrcmp(argv[i], "--no-widgets") == 0) { return AppPtr{new viewer::GuiApplication{argc, argv}}; } } return AppPtr{new viewer::Application{argc, argv}}; } spdlog::level::level_enum qtMsgTypeToSpdlogLevel(QtMsgType msgType) { switch (msgType) { case QtMsgType::QtDebugMsg: { return spdlog::level::debug; } case QtMsgType::QtWarningMsg: { return spdlog::level::warn; } case QtMsgType::QtCriticalMsg: { return spdlog::level::err; } case QtMsgType::QtFatalMsg: { return spdlog::level::critical; } case QtMsgType::QtInfoMsg: { return spdlog::level::info; } } INVARIANT(false, "Unknown QtMsgType {}", fmt::underlying(msgType)); } std::optional<QtMsgType> spdLogLevelToQtMsgType(spdlog::level::level_enum level) { switch (level) { case spdlog::level::trace: case spdlog::level::debug: { return QtMsgType::QtDebugMsg; } case spdlog::level::warn: { return QtMsgType::QtWarningMsg; } case spdlog::level::err: { return QtMsgType::QtCriticalMsg; } case spdlog::level::critical: { return QtMsgType::QtFatalMsg; } case spdlog::level::info: { return QtMsgType::QtInfoMsg; } case spdlog::level::off: { return std::nullopt; } case spdlog::level::n_levels: { break; } } INVARIANT(false, "Unknown spdlog::level::level_enum {}", fmt::underlying(level)); } class QtSink final : public spdlog::sinks::base_sink<spdlog::details::null_mutex> { protected: void sink_it_(const spdlog::details::log_msg & msg) override { auto msgType = spdLogLevelToQtMsgType(msg.level); if (!msgType) { return; } QMessageLogger messageLogger(msg.source.filename, msg.source.line, msg.source.funcname); auto message = QString::fromStdString(fmt::to_string(msg.payload)); switch (msgType.value()) { case QtMsgType::QtDebugMsg: { return messageLogger.debug("%s", qPrintable(message)); } case QtMsgType::QtWarningMsg: { return messageLogger.warning("%s", qPrintable(message)); } case QtMsgType::QtCriticalMsg: { return messageLogger.critical("%s", qPrintable(message)); } case QtMsgType::QtFatalMsg: { return messageLogger.fatal("%s", qPrintable(message)); } case QtMsgType::QtInfoMsg: { return messageLogger.info("%s", qPrintable(message)); } } INVARIANT(false, "unreachable"); } void flush_() override {} }; } // namespace int main(int argc, char * argv[]) { if ((true)) { QDirIterator resources{u":/"_s, QDir::Filter::AllEntries, QDirIterator::IteratorFlag::Subdirectories}; while (resources.hasNext()) { auto filePath = resources.next(); if (filePath.startsWith(u":/qt-project.org"_s)) { continue; } if (filePath.startsWith(u":/qpdf"_s)) { continue; } qCDebug(viewerMainCategory).noquote() << filePath; } } { auto projectName = QString::fromUtf8(sah_kd_tree::kProjectName); QVersionNumber applicationVersion{sah_kd_tree::kProjectVersionMajor, sah_kd_tree::kProjectVersionMinor, sah_kd_tree::kProjectVersionPatch, sah_kd_tree::kProjectVersionTweak}; QCoreApplication::setOrganizationName(projectName); QCoreApplication::setOrganizationDomain(projectName); QCoreApplication::setApplicationName(APPLICATION_NAME); QCoreApplication::setApplicationVersion(applicationVersion.toString()); } spdlog::level::level_enum spdlogActiveLevel = utils::autoCast(SPDLOG_ACTIVE_LEVEL); spdlog::set_level(spdlogActiveLevel); if ((true)) { // set env QT_ASSUME_STDERR_HAS_CONSOLE=1 or QT_FORCE_STDERR_LOGGING=1 if nothing is visible QString messagePattern; if (sah_kd_tree::kIsDebugBuild) { messagePattern = "[%{type}:%{category}] [file://%{file}:%{line}] %{message}"; } else { // The pattern can also be changed at runtime by setting the QT_MESSAGE_PATTERN environment variable; // if both qSetMessagePattern() is called and QT_MESSAGE_PATTERN is set, the environment variable takes precedence. messagePattern = "[%{time process} tid=%{threadid}] [%{type}:%{category}] %{message} (file://%{file}:%{line}" #ifndef QT_DEBUG " %{function}" #endif ")" #if __GLIBC__ R"(%{if-fatal}\n%{backtrace depth=32 separator="\n"}%{endif})"; #endif ; // "%{time yyyy/MM/dd dddd HH:mm:ss.zzz t}" } qSetMessagePattern(messagePattern); auto & sinks = spdlog::default_logger_raw()->sinks(); sinks.clear(); sinks.emplace_back(std::make_shared<QtSink>()); } else { if (!sah_kd_tree::kIsDebugBuild) { spdlog::default_logger_raw()->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [file://%g:%# (%!)] %v"); // (-logger name +full file path +func name) if comapre vs default "%+" format (spdlog::details::full_formatter) } constexpr QtMessageHandler messageHandler = [](QtMsgType msgType, const QMessageLogContext & messageLogContext, const QString & message) { auto lvl = qtMsgTypeToSpdlogLevel(msgType); if (!spdlog::should_log(lvl)) { return; } spdlog::source_loc location{messageLogContext.file, messageLogContext.line, messageLogContext.function}; auto category = messageLogContext.category ? messageLogContext.category : "default"; spdlog::log(location, lvl, "[{}] {}", category, qPrintable(message)); }; qInstallMessageHandler(messageHandler); } QSettings::setDefaultFormat(QSettings::Format::IniFormat); qCInfo(viewerMainCategory).noquote() << u"Settings path: %1"_s.arg(QSettings{}.fileName()); qCDebug(viewerMainCategory).noquote() << u"Current path: %1"_s.arg(QDir::currentPath()); auto resourcesBasePath = QUrl{u"qrc:///%1/"_s.arg(QString::fromUtf8(sah_kd_tree::kProjectName))}; auto application = createApplication(argc, argv); if (!application) { qFatal("unreachable"); } qCDebug(viewerMainCategory).noquote() << u"Application filepath: %1"_s.arg(QCoreApplication::applicationFilePath()); { auto applicationFont = application->font(); applicationFont.setFixedPitch(true); application->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); } QGuiApplication::setWindowIcon(QIcon{viewer::GuiApplication::getWindowIconFilepath()}); // QQuickStyle::setStyle("Material"); // QIcon::setThemeName("elementary"); QQuickWindow::setSceneGraphBackend("rhi"); QQuickWindow::setGraphicsApi(QSGRendererInterface::GraphicsApi::Vulkan); viewer::EngineWrapper engine; viewer::EngineSingletonForeign::setEngine(&engine); QVulkanInstance vulkanInstance; if (kUseEngine) { vulkanInstance.setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); auto & requiredInstanceExtensions = engine.getContext().requiredInstanceExtensions; requiredInstanceExtensions.insert(std::cend(requiredInstanceExtensions), {vk::KHRSurfaceExtensionName, vk::KHRXcbSurfaceExtensionName}); constexpr auto kApplicationVersion = vk::makeApiVersion(0, sah_kd_tree::kProjectVersionMajor, sah_kd_tree::kProjectVersionMinor, sah_kd_tree::kProjectVersionPatch); engine.getContext().createInstance(APPLICATION_NAME, kApplicationVersion, std::nullopt, nullptr, engine.getMutedMessageIdNumbers()); vulkanInstance.setVkInstance(engine.getContext().getInstance().getInstance()); } else { { QVersionNumber apiVersion(1, 3); ASSERT(apiVersion.isPrefixOf(vulkanInstance.supportedApiVersion())); vulkanInstance.setApiVersion(apiVersion); } { QByteArrayList layers; #ifndef NDEBUG layers.push_back("VK_LAYER_KHRONOS_validation"); #endif auto supportedLayers = vulkanInstance.supportedLayers(); for (const auto & layer : layers) { if (!supportedLayers.contains(layer)) { qCCritical(viewerMainCategory).noquote() << u"Layer %1 is not installed"_s.arg(QString::fromUtf8(layer)); return EXIT_FAILURE; } } vulkanInstance.setLayers(layers); } { // QRhiVulkanInitParams::preferredInstanceExtensions() auto instanceExtensions = QQuickGraphicsConfiguration::preferredInstanceExtensions(); auto supportedExtensions = vulkanInstance.supportedExtensions(); for (const auto & instanceExtension : std::as_const(instanceExtensions)) { if (!supportedExtensions.contains(instanceExtension)) { qCCritical(viewerMainCategory).noquote() << u"Instance extension %1 is not supported"_s.arg(QString::fromUtf8(instanceExtension)); return EXIT_FAILURE; } } vulkanInstance.setExtensions(instanceExtensions); } } if (!vulkanInstance.create()) { qCCritical(viewerMainCategory) << u"Cannot create Vulkan instance: %1"_s.arg(QString::fromStdString(fmt::to_string(vk::Result{vulkanInstance.errorCode()}))); return EXIT_FAILURE; } QQuickGraphicsConfiguration quickGraphicsConfiguration; quickGraphicsConfiguration.setDeviceExtensions({}); // set quickGraphicsConfiguration.setDepthBufferFor2D(true); // setDebugLayer QLoggingCategory::setFilterRules(u"qt.qml.binding.removal.info=true"_s); QQmlApplicationEngine qmlApplicationEngine; qmlApplicationEngine.setBaseUrl(resourcesBasePath); // qmlApplicationEngine.addImportPath(u":/%1/imports"_s.arg(QString::fromUtf8(sah_kd_tree::kProjectName))); const auto rootContext = qmlApplicationEngine.rootContext(); rootContext->setContextProperty("app", qApp); if (!QObject::connect(&qmlApplicationEngine, &QQmlApplicationEngine::objectCreationFailed, qApp, &QCoreApplication::quit, Qt::ConnectionType::QueuedConnection)) { qFatal("unreachable"); } const auto onObjectCreated = [&vulkanInstance, &quickGraphicsConfiguration, &engine](QObject * object, const QUrl & url) { if (!object) { qCCritical(viewerMainCategory).noquote() << u"Unable to create object from URL %1"_s.arg(url.toString()); return; } qCDebug(viewerMainCategory).noquote() << u"Object from URL %1 successfully created"_s.arg(url.toString()); auto applicationWindow = qobject_cast<QQuickWindow *>(object); INVARIANT(applicationWindow, "Expected QQuickWindow subclass"); INVARIANT(applicationWindow->objectName() == QCoreApplication::applicationName(), "Expected root ApplicationWindow component"); INVARIANT(!applicationWindow->isSceneGraphInitialized(), "Scene graph should not be initialized"); // TODO: QQuickRenderControl, QQuickWindow::setRenderTarget(QQuickRenderTarget::fromVulkanImage), // QQuickWindow::setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext), if ((false)) { // TODO: test applicationWindow->setPersistentGraphics(false); applicationWindow->setPersistentSceneGraph(false); } applicationWindow->setVulkanInstance(&vulkanInstance); if (kUseEngine) { auto & context = engine.getContext(); auto & requiredDeviceExtensions = context.requiredDeviceExtensions; requiredDeviceExtensions.insert(std::cend(requiredDeviceExtensions), {vk::KHRSwapchainExtensionName}); context.createDevice(QVulkanInstance::surfaceForWindow(applicationWindow)); vk::PhysicalDevice physicalDevice = context.getPhysicalDevice(); vk::Device device = context.getDevice(); const auto & queueCreateInfo = context.getPhysicalDevice().externalGraphicsQueueCreateInfo; INVARIANT(vulkanInstance.supportsPresent(physicalDevice, queueCreateInfo.familyIndex, applicationWindow), "Selected device and queue family cannot draw on surface"); auto quickGraphicsDevice = QQuickGraphicsDevice::fromDeviceObjects(physicalDevice, device, utils::autoCast(queueCreateInfo.familyIndex), utils::autoCast(queueCreateInfo.index)); applicationWindow->setGraphicsDevice(quickGraphicsDevice); } else { applicationWindow->setGraphicsConfiguration(quickGraphicsConfiguration); } engine.init(); }; if (!QObject::connect(&qmlApplicationEngine, &QQmlApplicationEngine::objectCreated, qApp, onObjectCreated)) { qFatal("unreachable"); } const auto saveSettings = [&qmlApplicationEngine] { auto rootObjects = qmlApplicationEngine.rootObjects(); INVARIANT(std::size(rootObjects) == 1, "Expected single object, got: {}", std::size(rootObjects)); auto applicationWindow = qobject_cast<const QQuickWindow *>(rootObjects.first()); INVARIANT(applicationWindow, "Expected QQuickWindow subclass"); // examine applicationWindow properties }; if (!QObject::connect(qApp, &QCoreApplication::aboutToQuit, &engine, saveSettings)) { qFatal("unreachable"); } { QVariantMap initialProperties; initialProperties["visible"] = true; qmlApplicationEngine.setInitialProperties(initialProperties); } qmlApplicationEngine.load(QUrl{"qml/ui.qml"}); return application->exec(); }