From 4f62f199b82f20a838fa4aaa22f8e84f82b542d1 Mon Sep 17 00:00:00 2001 From: Rhys Mainwaring Date: Thu, 5 Aug 2021 14:38:23 +0100 Subject: [PATCH 1/7] [Metal] Add Metal support to MinimalScene and Qt Application MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow MinimalScene to use either OpenGL or Metal render systems Add classes to abstract out render hardware interfaces: - IgnCameraTextureRhi - RenderThreadRhi - TextureNodeRhi Add implementations for OpenGL and Metal in: - MinimalSceneRhiOpenGL - MinimalSceneRhiMetal - Modify the existing MinimalScene class to forward rendering calls to the (virtual) render hardware interface functions. - Modify plugin CMakeLists.txt to conditionally compile Metal support if the platform is macOS - Update Application.cc to support either OpenGL or Metal Refactor MinimalScene to use GraphicAPI - Use the GraphicsAPI enum to set the choice of graphics interface (OpenGL or Metal). - Change the MinimalScene plugin element to ... Add documentation and confirm to code style - Document the OpenGL and Metal render interface classes - Revert default engine to ogre Set graphics API to Metal for macOS, otherwise use OpenGL - Update Application.cc to only use Metal for macOS - Fix issues with code check Remove unused variable from Application.cc Change pointer casts to satisfy code check - Code check requires we use reinterpret_cast rather than static_cast. Combine initialisation methods - Revert to original spelling and names for Initialize() - In RenderItem call Ready() using invokeMethod when not using Metal - Attempting to resolve residual QSG threading issue (use of threaded QSG is inconsistent between platforms) Force both OpenGL and Metal to use QSG_RENDER_LOOP "basic" - Force QSG to run on the main application thread - This will resolve the exception on Ubuntu but a better solution is required. Resolve issue with QSG render loop for Metal - Metal version now supports "threaded" QSG_RENDER_LOOP - OpenGL version still unresolved. Update src/plugins/minimal_scene/MinimalScene.hh Co-authored-by: Alejandro Hernández Cordero Update src/plugins/minimal_scene/MinimalSceneRhi.hh Co-authored-by: Alejandro Hernández Cordero Fix issues with OpenGL context thread affinity - The OpenGL context must be moved from the QSGRenderThread to the main thread for initialisation - After initialisation the OpenGL context is then moved to the Ignition render thread Signed-off-by: Rhys Mainwaring --- src/Application.cc | 23 ++ src/plugins/minimal_scene/CMakeLists.txt | 33 +- src/plugins/minimal_scene/MinimalScene.cc | 328 ++++++++++++------ src/plugins/minimal_scene/MinimalScene.hh | 69 +++- src/plugins/minimal_scene/MinimalSceneRhi.cc | 55 +++ src/plugins/minimal_scene/MinimalSceneRhi.hh | 128 +++++++ .../minimal_scene/MinimalSceneRhiMetal.hh | 135 +++++++ .../minimal_scene/MinimalSceneRhiMetal.mm | 223 ++++++++++++ .../minimal_scene/MinimalSceneRhiOpenGL.cc | 282 +++++++++++++++ .../minimal_scene/MinimalSceneRhiOpenGL.hh | 147 ++++++++ 10 files changed, 1302 insertions(+), 121 deletions(-) create mode 100644 src/plugins/minimal_scene/MinimalSceneRhi.cc create mode 100644 src/plugins/minimal_scene/MinimalSceneRhi.hh create mode 100644 src/plugins/minimal_scene/MinimalSceneRhiMetal.hh create mode 100644 src/plugins/minimal_scene/MinimalSceneRhiMetal.mm create mode 100644 src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc create mode 100644 src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh diff --git a/src/Application.cc b/src/Application.cc index 9b8d0c79b..cd17ba747 100644 --- a/src/Application.cc +++ b/src/Application.cc @@ -87,6 +87,29 @@ Application::Application(int &_argc, char **_argv, const WindowType _type) { igndbg << "Initializing application." << std::endl; +#if __APPLE__ + // Use the Metal graphics API on macOS. + igndbg << "Qt using Metal graphics interface" << std::endl; + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::MetalRhi); + + // TODO(srmainwaring): implement facility for overriding the default + // graphics API in macOS, in which case there are restrictions on + // the version of OpenGL used. + // + // macOS only supports OpenGL <= 4.1. + // This must be called before the main window is shown. + // QSurfaceFormat format(QSurfaceFormat::DeprecatedFunctions); + // format.setDepthBufferSize(24); + // format.setStencilBufferSize(8); + // format.setVersion(4, 1); + // format.setProfile(QSurfaceFormat::CoreProfile); + // format.setRenderableType(QSurfaceFormat::OpenGL); + // QSurfaceFormat::setDefaultFormat(format); +#else + // Otherwise use OpenGL + igndbg << "Qt using OpenGL graphics interface" << std::endl; +#endif + // Configure console common::Console::SetPrefix("[GUI] "); diff --git a/src/plugins/minimal_scene/CMakeLists.txt b/src/plugins/minimal_scene/CMakeLists.txt index 7f98b9908..a5ac7cd4b 100644 --- a/src/plugins/minimal_scene/CMakeLists.txt +++ b/src/plugins/minimal_scene/CMakeLists.txt @@ -1,9 +1,40 @@ +set(SOURCES + MinimalScene.cc + MinimalSceneRhi.cc + MinimalSceneRhiOpenGL.cc +) + +set(PROJECT_LINK_LIBS "") + +# Objective-C sources for macOS +if (APPLE) + set(SOURCES + ${SOURCES} + MinimalSceneRhiMetal.mm + ) + + set(PROJECT_LINK_LIBS + "-framework AppKit" + "-framework Metal" + ) +endif() + ign_gui_add_plugin(MinimalScene SOURCES - MinimalScene.cc + ${SOURCES} QT_HEADERS MinimalScene.hh PUBLIC_LINK_LIBS ignition-rendering${IGN_RENDERING_VER}::ignition-rendering${IGN_RENDERING_VER} + ${PROJECT_LINK_LIBS} ) +# Enable ARC on selected source files +if (APPLE) + set_source_files_properties( + MinimalSceneRhiMetal.mm + PROPERTIES + COMPILE_FLAGS + "-fobjc-arc" + ) +endif() diff --git a/src/plugins/minimal_scene/MinimalScene.cc b/src/plugins/minimal_scene/MinimalScene.cc index f34a818b1..69c8725e5 100644 --- a/src/plugins/minimal_scene/MinimalScene.cc +++ b/src/plugins/minimal_scene/MinimalScene.cc @@ -16,6 +16,9 @@ */ #include "MinimalScene.hh" +#include "MinimalSceneRhi.hh" +#include "MinimalSceneRhiMetal.hh" +#include "MinimalSceneRhiOpenGL.hh" #include #include @@ -92,6 +95,12 @@ class ignition::gui::plugins::IgnRenderer::Implementation /// \brief View control focus target public: math::Vector3d target; + + /// \brief Render system parameters + public: std::map rhiParams; + + /// \brief Render hardware interface for the texture + public: std::unique_ptr rhi; }; /// \brief Qt and Ogre rendering is happening in different threads @@ -183,6 +192,16 @@ class ignition::gui::plugins::RenderWindowItem::Implementation /// \brief Keep latest mouse event public: common::MouseEvent mouseEvent; + /// \brief True if initialized + public: bool initialized = false; + + /// \brief True if initializing (started but not complete) + public: bool initializing = false; + + /// \brief Graphics API + public: ignition::rendering::GraphicsAPI graphicsAPI = + rendering::GraphicsAPI::OPENGL; + /// \brief Render thread public: RenderThread *renderThread = nullptr; @@ -239,7 +258,6 @@ void RenderSync::WaitForWorkerThread() // Worker thread asked us to wait! this->renderStallState = RenderStallState::WorkerCanProceed; - lock.unlock(); // Wake up worker thread this->cv.notify_one(); @@ -270,6 +288,8 @@ void RenderSync::Shutdown() IgnRenderer::IgnRenderer() : dataPtr(utils::MakeUniqueImpl()) { + // Set default graphics API to OpenGL + this->SetGraphicsAPI(rendering::GraphicsAPI::OPENGL); } ///////////////////////////////////////////////// @@ -300,7 +320,8 @@ void IgnRenderer::Render(RenderSync *_renderSync) // _renderSync->ReleaseQtThreadFromBlock(lock); } - this->textureId = this->dataPtr->camera->RenderTextureGLId(); + // Update the render interface (texture) + this->dataPtr->rhi->Update(this->dataPtr->camera); // view control this->HandleMouseEvent(); @@ -514,12 +535,11 @@ void IgnRenderer::Initialize() if (this->initialized) return; - std::map params; - params["useCurrentGLContext"] = "1"; - params["winID"] = std::to_string( + this->dataPtr->rhiParams["winID"] = std::to_string( ignition::gui::App()->findChild()-> QuickWindow()->winId()); - auto engine = rendering::engine(this->engineName, params); + auto engine = rendering::engine( + this->engineName, this->dataPtr->rhiParams); if (!engine) { ignerr << "Engine [" << this->engineName << "] is not supported" @@ -558,7 +578,9 @@ void IgnRenderer::Initialize() // setting the size and calling PreRender should cause the render texture to // be rebuilt this->dataPtr->camera->PreRender(); - this->textureId = this->dataPtr->camera->RenderTextureGLId(); + + // Update the render interface (texture) + this->dataPtr->rhi->Update(this->dataPtr->camera); // Ray Query this->dataPtr->rayQuery = this->dataPtr->camera->Scene()->CreateRayQuery(); @@ -566,6 +588,28 @@ void IgnRenderer::Initialize() this->initialized = true; } +///////////////////////////////////////////////// +void IgnRenderer::SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsAPI) +{ + // Create render interface and reset params + this->dataPtr->rhiParams.clear(); + + if (_graphicsAPI == rendering::GraphicsAPI::OPENGL) + { + qDebug().nospace() << "Creating ign-renderering interface for OpenGL"; + this->dataPtr->rhiParams["useCurrentGLContext"] = "1"; + this->dataPtr->rhi = std::make_unique(); + } +#ifdef __APPLE__ + else if (_graphicsAPI == rendering::GraphicsAPI::METAL) + { + qDebug().nospace() << "Creating ign-renderering interface for Metal"; + this->dataPtr->rhiParams["metal"] = "1"; + this->dataPtr->rhi = std::make_unique(); + } +#endif +} + ///////////////////////////////////////////////// void IgnRenderer::Destroy() { @@ -642,9 +686,18 @@ math::Vector3d IgnRenderer::ScreenToScene( this->dataPtr->rayQuery->Direction() * 10; } +///////////////////////////////////////////////// +void IgnRenderer::TextureId(void* _texturePtr) +{ + this->dataPtr->rhi->TextureId(_texturePtr); +} + ///////////////////////////////////////////////// RenderThread::RenderThread() { + // Set default graphics API to OpenGL + this->SetGraphicsAPI(rendering::GraphicsAPI::OPENGL); + RenderWindowItem::Implementation::threads << this; qRegisterMetaType("RenderSync*"); } @@ -652,38 +705,17 @@ RenderThread::RenderThread() ///////////////////////////////////////////////// void RenderThread::RenderNext(RenderSync *_renderSync) { - this->context->makeCurrent(this->surface); - - if (!this->ignRenderer.initialized) - { - // Initialize renderer - this->ignRenderer.Initialize(); - } - - // check if engine has been successfully initialized - if (!this->ignRenderer.initialized) - { - ignerr << "Unable to initialize renderer" << std::endl; - return; - } - - this->ignRenderer.Render(_renderSync); - - emit TextureReady(this->ignRenderer.textureId, this->ignRenderer.textureSize); + this->rhi->RenderNext(_renderSync); + emit this->TextureReady( + this->rhi->TexturePtr(), + this->rhi->TextureSize()); } ///////////////////////////////////////////////// void RenderThread::ShutDown() { - this->context->makeCurrent(this->surface); - - this->ignRenderer.Destroy(); - - this->context->doneCurrent(); - delete this->context; - - // schedule this to be deleted only after we're done cleaning up - this->surface->deleteLater(); + // The render interface calls Destroy on IgnRendering + this->rhi->ShutDown(); // Stop event processing, move the thread to GUI and make sure it is deleted. this->exit(); @@ -708,34 +740,87 @@ void RenderThread::SizeChanged() } ///////////////////////////////////////////////// -TextureNode::TextureNode(QQuickWindow *_window, RenderSync &_renderSync) - : renderSync(_renderSync), window(_window) -{ - // Our texture node must have a texture, so use the default 0 texture. -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) - this->texture = this->window->createTextureFromId(0, QSize(1, 1)); -#else - void * nativeLayout; - this->texture = this->window->createTextureFromNativeObject( - QQuickWindow::NativeObjectTexture, &nativeLayout, 0, QSize(1, 1), - QQuickWindow::TextureIsOpaque); +QOffscreenSurface *RenderThread::Surface() const +{ + return this->rhi->Surface(); +} + +///////////////////////////////////////////////// +void RenderThread::SetSurface(QOffscreenSurface *_surface) +{ + this->rhi->SetSurface(_surface); +} + +///////////////////////////////////////////////// +QOpenGLContext *RenderThread::Context() const +{ + return this->rhi->Context(); +} + +///////////////////////////////////////////////// +void RenderThread::SetContext(QOpenGLContext *_context) +{ + this->rhi->SetContext(_context); +} + +///////////////////////////////////////////////// +void RenderThread::SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsAPI) +{ + // Set the graphics API for the IgnRenderer + this->ignRenderer.SetGraphicsAPI(_graphicsAPI); + + // Create the render interface + if (_graphicsAPI == rendering::GraphicsAPI::OPENGL) + { + qDebug().nospace() << "Creating render thread interface for OpenGL"; + this->rhi = std::make_unique(&this->ignRenderer); + } +#ifdef __APPLE__ + else if (_graphicsAPI == rendering::GraphicsAPI::METAL) + { + qDebug().nospace() << "Creating render thread interface for Metal"; + this->rhi = std::make_unique(&this->ignRenderer); + } #endif - this->setTexture(this->texture); } ///////////////////////////////////////////////// -TextureNode::~TextureNode() +void RenderThread::Initialize() { - delete this->texture; + this->rhi->Initialize(); +} + +///////////////////////////////////////////////// +TextureNode::TextureNode( + QQuickWindow *_window, + RenderSync &_renderSync, + const rendering::GraphicsAPI &_graphicsAPI) + : renderSync(_renderSync) + , window(_window) +{ + if (_graphicsAPI == rendering::GraphicsAPI::OPENGL) + { + qDebug().nospace() << "Creating texture node render interface for OpenGL"; + this->rhi = std::make_unique(_window); + } +#ifdef __APPLE__ + else if (_graphicsAPI == rendering::GraphicsAPI::METAL) + { + qDebug().nospace() << "Creating texture node render interface for Metal"; + this->rhi = std::make_unique(_window); + } +#endif + + this->setTexture(this->rhi->Texture()); } ///////////////////////////////////////////////// -void TextureNode::NewTexture(uint _id, const QSize &_size) +TextureNode::~TextureNode() = default; + +///////////////////////////////////////////////// +void TextureNode::NewTexture(void* _texturePtr, const QSize &_size) { - this->mutex.lock(); - this->id = _id; - this->size = _size; - this->mutex.unlock(); + this->rhi->NewTexture(_texturePtr, _size); // We cannot call QQuickWindow::update directly here, as this is only allowed // from the rendering thread or GUI thread. @@ -745,34 +830,11 @@ void TextureNode::NewTexture(uint _id, const QSize &_size) ///////////////////////////////////////////////// void TextureNode::PrepareNode() { - this->mutex.lock(); - uint newId = this->id; - QSize sz = this->size; - this->id = 0; - this->mutex.unlock(); - if (newId) - { - delete this->texture; - // note: include QQuickWindow::TextureHasAlphaChannel if the rendered - // content has alpha. -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) - this->texture = this->window->createTextureFromId( - newId, sz, QQuickWindow::TextureIsOpaque); -#else - // TODO(anyone) Use createTextureFromNativeObject - // https://github.com/ignitionrobotics/ign-gui/issues/113 -#ifndef _WIN32 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - this->texture = this->window->createTextureFromId( - newId, sz, QQuickWindow::TextureIsOpaque); -#ifndef _WIN32 -# pragma GCC diagnostic pop -#endif + this->rhi->PrepareNode(); -#endif - this->setTexture(this->texture); + if (this->rhi->HasNewTexture()) + { + this->setTexture(this->rhi->Texture()); this->markDirty(DirtyMaterial); @@ -830,24 +892,40 @@ RenderWindowItem::~RenderWindowItem() } ///////////////////////////////////////////////// +// This slot will run on the main thread void RenderWindowItem::Ready() { - this->dataPtr->renderThread->surface = new QOffscreenSurface(); - this->dataPtr->renderThread->surface->setFormat( - this->dataPtr->renderThread->context->format()); - this->dataPtr->renderThread->surface->create(); + if (this->dataPtr->graphicsAPI == rendering::GraphicsAPI::OPENGL) + { + this->dataPtr->renderThread->SetSurface(new QOffscreenSurface()); + this->dataPtr->renderThread->Surface()->setFormat( + this->dataPtr->renderThread->Context()->format()); + this->dataPtr->renderThread->Surface()->create(); + } - this->dataPtr->renderThread->ignRenderer.textureSize = - QSize(std::max({this->width(), 1.0}), std::max({this->height(), 1.0})); + // Carry out initialization on main thread before moving to render thread + this->dataPtr->renderThread->Initialize(); + + if (this->dataPtr->graphicsAPI == rendering::GraphicsAPI::OPENGL) + { + // Move context to the render thread + this->dataPtr->renderThread->Context()->moveToThread( + this->dataPtr->renderThread); + } this->dataPtr->renderThread->moveToThread(this->dataPtr->renderThread); + this->dataPtr->renderThread->ignRenderer.textureSize = + QSize(std::max({this->width(), 1.0}), std::max({this->height(), 1.0})); + this->connect(this, &QQuickItem::widthChanged, this->dataPtr->renderThread, &RenderThread::SizeChanged); this->connect(this, &QQuickItem::heightChanged, this->dataPtr->renderThread, &RenderThread::SizeChanged); this->dataPtr->renderThread->start(); + this->dataPtr->initializing = false; + this->dataPtr->initialized = true; this->update(); } @@ -857,30 +935,57 @@ QSGNode *RenderWindowItem::updatePaintNode(QSGNode *_node, { TextureNode *node = static_cast(_node); - if (!this->dataPtr->renderThread->context) + if (!this->dataPtr->initialized) { - QOpenGLContext *current = this->window()->openglContext(); - // Some GL implementations require that the currently bound context is - // made non-current before we set up sharing, so we doneCurrent here - // and makeCurrent down below while setting up our own context. - current->doneCurrent(); - - this->dataPtr->renderThread->context = new QOpenGLContext(); - this->dataPtr->renderThread->context->setFormat(current->format()); - this->dataPtr->renderThread->context->setShareContext(current); - this->dataPtr->renderThread->context->create(); - this->dataPtr->renderThread->context->moveToThread( - this->dataPtr->renderThread); + // Exit immediately if still initializing + if (this->dataPtr->initializing) + { + return nullptr; + } + this->dataPtr->initializing = true; + + // Set the render thread's render system + this->dataPtr->renderThread->SetGraphicsAPI( + this->dataPtr->graphicsAPI); + + if (this->dataPtr->graphicsAPI == rendering::GraphicsAPI::OPENGL) + { + QOpenGLContext *current = this->window()->openglContext(); + // Some GL implementations require that the currently bound context is + // made non-current before we set up sharing, so we doneCurrent here + // and makeCurrent down below while setting up our own context. + current->doneCurrent(); + + this->dataPtr->renderThread->SetContext(new QOpenGLContext()); + this->dataPtr->renderThread->Context()->setFormat(current->format()); + this->dataPtr->renderThread->Context()->setShareContext(current); + this->dataPtr->renderThread->Context()->create(); + + // The slot "Ready" runs on the main thread, move the context to match + this->dataPtr->renderThread->Context()->moveToThread( + QApplication::instance()->thread()); - current->makeCurrent(this->window()); + current->makeCurrent(this->window()); - QMetaObject::invokeMethod(this, "Ready"); + // Initialize on main thread + QMetaObject::invokeMethod(this, "Ready", Qt::QueuedConnection); + } + else if (this->dataPtr->graphicsAPI == rendering::GraphicsAPI::METAL) + { + // Initialize on main thread + QMetaObject::invokeMethod(this, "Ready", Qt::QueuedConnection); + } + else + { + // invalid render system + } return nullptr; } if (!node) { - node = new TextureNode(this->window(), this->dataPtr->renderSync); + node = new TextureNode(this->window(), this->dataPtr->renderSync, + this->dataPtr->graphicsAPI); // Set up connections to get the production of render texture in sync with // vsync on the rendering thread. @@ -995,6 +1100,14 @@ void RenderWindowItem::SetSkyEnabled(const bool &_sky) this->dataPtr->renderThread->ignRenderer.skyEnable = _sky; } +///////////////////////////////////////////////// +void RenderWindowItem::SetGraphicsAPI( + const rendering::GraphicsAPI &_graphicsAPI) +{ + this->dataPtr->graphicsAPI = _graphicsAPI; + this->dataPtr->renderThread->SetGraphicsAPI(_graphicsAPI); +} + ///////////////////////////////////////////////// MinimalScene::MinimalScene() : Plugin(), dataPtr(utils::MakeUniqueImpl()) @@ -1138,7 +1251,16 @@ void MinimalScene::LoadConfig(const tinyxml2::XMLElement *_pluginElem) { renderWindow->SetSkyEnabled(true); if (!elem->NoChildren()) - ignwarn << "Child elements of are not supported yet" << std::endl; + ignwarn << "Child elements of are not supported yet" + << std::endl; + } + + elem = _pluginElem->FirstChildElement("graphics_api"); + if (nullptr != elem && nullptr != elem->GetText()) + { + rendering::GraphicsAPI graphicsAPI = + rendering::GraphicsAPIUtils::Set(elem->GetText()); + renderWindow->SetGraphicsAPI(graphicsAPI); } } diff --git a/src/plugins/minimal_scene/MinimalScene.hh b/src/plugins/minimal_scene/MinimalScene.hh index 49b72198b..55ba0cad0 100644 --- a/src/plugins/minimal_scene/MinimalScene.hh +++ b/src/plugins/minimal_scene/MinimalScene.hh @@ -27,9 +27,12 @@ #include #include #include +#include #include "ignition/gui/Plugin.hh" +#include "MinimalSceneRhi.hh" + namespace ignition { namespace gui @@ -105,9 +108,14 @@ namespace plugins /// synchronize Qt and worker thread (this) public: void Render(RenderSync *_renderSync); - /// \brief Initialize the render engine + /// \brief Initialize the render engine and scene. + /// On macOS this must be called on the main thread. public: void Initialize(); + /// \brief Set the graphics API + /// \param[in] _graphicsApi The type of graphics API + public: void SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsAPI); + /// \brief Destroy camera associated with this renderer public: void Destroy(); @@ -173,7 +181,8 @@ namespace plugins /// Values is constantly constantly cycled/swapped/changed /// from a worker thread /// Don't read this directly - public: GLuint textureId; + /// \param[out] _texturePtr Pointer to a texture Id + public: void TextureId(void* _texturePtr); /// \brief Render engine to use public: std::string engineName = "ogre"; @@ -203,7 +212,7 @@ namespace plugins public: QSize textureSize = QSize(1024, 1024); /// \brief Flag to indicate texture size has changed. - public: bool textureDirty = false; + public: bool textureDirty = true; /// \brief Scene service. If not empty, a request will be made to get the /// scene information using this service and the renderer will populate the @@ -251,18 +260,40 @@ namespace plugins /// \brief Signal to indicate that a frame has been rendered and ready /// to be displayed - /// \param[in] _id GLuid of the opengl texture + /// \param[in] _texturePtr Pointer to a texture Id /// \param[in] _size Size of the texture - signals: void TextureReady(uint _id, const QSize &_size); + signals: void TextureReady(void* _texturePtr, const QSize &_size); /// \brief Offscreen surface to render to - public: QOffscreenSurface *surface = nullptr; + public: QOffscreenSurface *Surface() const; + + /// \brief Set the offscreen surface to render to + // + /// \param[in] _surface Off-screen surface format + public: void SetSurface(QOffscreenSurface *_surface); /// \brief OpenGL context to be passed to the render engine - public: QOpenGLContext *context = nullptr; + public: QOpenGLContext *Context() const; + + /// \brief Set the OpenGL context to be passed to the render engine + // + /// \param[in] _context OpenGL context + public: void SetContext(QOpenGLContext *_context); + + /// \brief Set the graphics API + /// \param[in] _graphicsApi The type of graphics API + public: void SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsApi); + + /// \brief Carry out initialisation. + // + /// On macOS this must be run on the main thread + public: void Initialize(); /// \brief Ign-rendering renderer public: IgnRenderer ignRenderer; + + /// \brief Pointer to render interface to handle OpenGL/Metal compatibility + private: std::unique_ptr rhi; }; /// \brief A QQUickItem that manages the render window @@ -342,6 +373,10 @@ namespace plugins /// \param[in] _sky True to enable the sky, false otherwise. public: void SetSkyEnabled(const bool &_sky); + /// \brief Set the graphics API + /// \param[in] _graphicsApi The type of graphics API + public: void SetGraphicsAPI(const rendering::GraphicsAPI& _graphicsAPI); + /// \brief Slot called when thread is ready to be started public Q_SLOTS: void Ready(); @@ -394,17 +429,20 @@ namespace plugins /// \param[in] _window Window to display the texture /// \param[in] _renderSync RenderSync to safely /// synchronize Qt (this) and worker thread + /// \param[in] _graphicsAPI The type of graphics API public: explicit TextureNode(QQuickWindow *_window, - RenderSync &_renderSync); + RenderSync &_renderSync, + const rendering::GraphicsAPI &_graphicsAPI); /// \brief Destructor public: ~TextureNode() override; /// \brief This function gets called on the FBO rendering thread and will /// store the texture id and size and schedule an update on the window. - /// \param[in] _id OpenGL render texture Id + /// \param[in] _texturePtr Pointer to a texture Id /// \param[in] _size Texture size - public slots: void NewTexture(uint _id, const QSize &_size); + // public slots: void NewTexture(uint _id, const QSize &_size); + public slots: void NewTexture(void* _texturePtr, const QSize &_size); /// \brief Before the scene graph starts to render, we update to the /// pending texture @@ -417,9 +455,6 @@ namespace plugins /// update signals: void PendingNewTexture(); - /// \brief OpenGL texture id - public: uint id = 0; - /// \brief Texture size public: QSize size = QSize(0, 0); @@ -429,12 +464,12 @@ namespace plugins /// \brief See RenderSync public: RenderSync &renderSync; - /// \brief Qt's scene graph texture - public: QSGTexture *texture = nullptr; - /// \brief Qt quick window public: QQuickWindow *window = nullptr; - }; + + /// \brief Pointer to render interface to handle OpenGL/Metal compatibility + private: std::unique_ptr rhi; + }; } } } diff --git a/src/plugins/minimal_scene/MinimalSceneRhi.cc b/src/plugins/minimal_scene/MinimalSceneRhi.cc new file mode 100644 index 000000000..3026963d2 --- /dev/null +++ b/src/plugins/minimal_scene/MinimalSceneRhi.cc @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include "MinimalSceneRhi.hh" + +using namespace ignition; +using namespace gui; +using namespace plugins; + +///////////////////////////////////////////////// +IgnCameraTextureRhi::~IgnCameraTextureRhi() = default; + +///////////////////////////////////////////////// +RenderThreadRhi::~RenderThreadRhi() = default; + +///////////////////////////////////////////////// +QOffscreenSurface *RenderThreadRhi::Surface() const +{ + return reinterpret_cast(0); +} + +///////////////////////////////////////////////// +void RenderThreadRhi::SetSurface(QOffscreenSurface *) +{ + /* no-op */ +} + +///////////////////////////////////////////////// +QOpenGLContext *RenderThreadRhi::Context() const +{ + return reinterpret_cast(0); +} + +///////////////////////////////////////////////// +void RenderThreadRhi::SetContext(QOpenGLContext *) +{ + /* no-op */ +} + +///////////////////////////////////////////////// +TextureNodeRhi::~TextureNodeRhi() = default; diff --git a/src/plugins/minimal_scene/MinimalSceneRhi.hh b/src/plugins/minimal_scene/MinimalSceneRhi.hh new file mode 100644 index 000000000..1b016bc68 --- /dev/null +++ b/src/plugins/minimal_scene/MinimalSceneRhi.hh @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef IGNITION_GUI_PLUGINS_MINIMALSCENE_MINIMALSCENERHI_HH_ +#define IGNITION_GUI_PLUGINS_MINIMALSCENE_MINIMALSCENERHI_HH_ + +#include "ignition/gui/Plugin.hh" +#include "ignition/rendering/RenderTypes.hh" + +#include +#include + +namespace ignition +{ +namespace gui +{ +namespace plugins +{ + /// \brief Render interface class to handle OpenGL / Metal compatibility + /// of camera textures in IgnRenderer + // + /// Each supported graphics API must implement this interface + /// to provide access to the underlying render system's texture. + class IgnCameraTextureRhi + { + /// \brief Destructor + public: virtual ~IgnCameraTextureRhi(); + + /// \brief Update the texture for a camera + /// \param[in] _camera Pointer to the camera providing the texture + public: virtual void Update(rendering::CameraPtr _camera) = 0; + + /// \brief Get the graphics API texture Id + /// \param[out] _texturePtr Pointer to a texture Id + public: virtual void TextureId(void* _texturePtr) = 0; + }; + + /// \brief Ign-rendering renderer. + class IgnRenderer; + class RenderSync; + + /// \brief Render interface class to handle OpenGL / Metal compatibility + /// in RenderThread + class RenderThreadRhi + { + /// \brief Destructor + public: virtual ~RenderThreadRhi(); + + /// \brief Offscreen surface to render to + public: virtual QOffscreenSurface *Surface() const; + + /// \brief Set the offscreen surface to render to + // + /// \param[in] _surface Off-screen surface format + public: virtual void SetSurface(QOffscreenSurface *_surface); + + /// \brief OpenGL context to be passed to the render engine + public: virtual QOpenGLContext *Context() const; + + /// \brief Set the OpenGL context to be passed to the render engine + // + /// \param[in] _context OpenGL context + public: virtual void SetContext(QOpenGLContext *_context); + + /// \brief Carry out initialization + // + /// On macOS this must be run on the main thread + public: virtual void Initialize() = 0; + + /// \brief Render when safe + /// \param[in] _renderSync RenderSync to safely + /// synchronize Qt and worker thread (this) + public: virtual void RenderNext(RenderSync *_renderSync) = 0; + + /// \brief Return a pointer to the graphics API texture Id + public: virtual void* TexturePtr() const = 0; + + /// \brief Return the size of the texture + public: virtual QSize TextureSize() const = 0; + + /// \brief Shutdown the thread and the render engine + public: virtual void ShutDown() = 0; + }; + + /// \brief Render interface class to handle OpenGL / Metal compatibility + /// in TextureNode + class TextureNodeRhi + { + /// \brief Destructor + public: virtual ~TextureNodeRhi(); + + /// \brief Get the Qt scene graph texture + public: virtual QSGTexture *Texture() const = 0; + + /// \brief Return true if a new texture has been received + /// from the render thread + public: virtual bool HasNewTexture() const = 0; + + /// \brief This function gets called on the render thread and will + /// store the texture id and size and schedule an update on the window. + /// \param[in] _texturePtr Pointer to a texture Id + /// \param[in] _size Texture size + public: virtual void NewTexture( + void* _texturePtr, const QSize &_size) = 0; + + /// \brief Before the scene graph starts to render, we update to the + /// pending texture + public: virtual void PrepareNode() = 0; + }; +} +} +} + +#endif diff --git a/src/plugins/minimal_scene/MinimalSceneRhiMetal.hh b/src/plugins/minimal_scene/MinimalSceneRhiMetal.hh new file mode 100644 index 000000000..81097841d --- /dev/null +++ b/src/plugins/minimal_scene/MinimalSceneRhiMetal.hh @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef IGNITION_GUI_PLUGINS_MINIMALSCENE_MINIMALSCENERHIMETAL_HH_ +#define IGNITION_GUI_PLUGINS_MINIMALSCENE_MINIMALSCENERHIMETAL_HH_ + +#include "MinimalSceneRhi.hh" +#include "ignition/gui/Plugin.hh" + +#include +#include +#include + +#include + +namespace ignition +{ +namespace gui +{ +namespace plugins +{ + /// \brief Private data for IgnCameraTextureRhiMetal + class IgnCameraTextureRhiMetalPrivate; + + /// \brief Implementation of IgnCameraTextureRhi for the Metal graphics API + class IgnCameraTextureRhiMetal : public IgnCameraTextureRhi + { + // Documentation inherited + public: virtual ~IgnCameraTextureRhiMetal() override; + + /// \brief Constructor + public: IgnCameraTextureRhiMetal(); + + // Documentation inherited + public: virtual void Update(rendering::CameraPtr _camera) override; + + // Documentation inherited + public: virtual void TextureId(void* _texturePtr) override; + + /// \internal Pointer to private data + private: std::unique_ptr dataPtr; + }; + + /// \brief Private data for RenderThreadRhiMetal + class RenderThreadRhiMetalPrivate; + + /// \brief Implementation of RenderThreadRhi for the Metal graphics API + class RenderThreadRhiMetal : public RenderThreadRhi + { + // Documentation inherited + public: virtual ~RenderThreadRhiMetal() override; + + /// \brief Constructor + /// \param[in] _renderer The Ign-rendering renderer + public: RenderThreadRhiMetal(IgnRenderer *_renderer); + + // Documentation inherited + public: virtual void Initialize() override; + + // Documentation inherited + public: virtual void RenderNext(RenderSync *_renderSync) override; + + // Documentation inherited + public: virtual void* TexturePtr() const override; + + // Documentation inherited + public: virtual QSize TextureSize() const override; + + // Documentation inherited + public: virtual void ShutDown() override; + + /// \internal Prevent copy and assignment + private: RenderThreadRhiMetal( + const RenderThreadRhiMetal &_other) = delete; + private: RenderThreadRhiMetal& operator=( + const RenderThreadRhiMetal &_other) = delete; + + /// \internal Pointer to private data + private: std::unique_ptr dataPtr; + }; + + /// \brief Private data for TextureNodeRhiMetal + class TextureNodeRhiMetalPrivate; + + /// \brief Implementation of TextureNodeRhi for the Metal graphics API + class TextureNodeRhiMetal : public TextureNodeRhi + { + // Documentation inherited + public: virtual ~TextureNodeRhiMetal() override; + + /// \brief Constructor + /// \param[in] _window Window to display the texture + public: TextureNodeRhiMetal(QQuickWindow *_window); + + // Documentation inherited + public: virtual QSGTexture *Texture() const override; + + // Documentation inherited + public: virtual bool HasNewTexture() const override; + + // Documentation inherited + public: virtual void NewTexture( + void* _texturePtr, const QSize &_size)override; + + // Documentation inherited + public: virtual void PrepareNode() override; + + /// \internal Prevent copy and assignment + private: TextureNodeRhiMetal( + const TextureNodeRhiMetal &_other) = delete; + private: TextureNodeRhiMetal& operator=( + const TextureNodeRhiMetal &_other) = delete; + + /// \internal Pointer to private data + private: std::unique_ptr dataPtr; + }; +} +} +} + +#endif diff --git a/src/plugins/minimal_scene/MinimalSceneRhiMetal.mm b/src/plugins/minimal_scene/MinimalSceneRhiMetal.mm new file mode 100644 index 000000000..0acdd4f65 --- /dev/null +++ b/src/plugins/minimal_scene/MinimalSceneRhiMetal.mm @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include "MinimalSceneRhiMetal.hh" +#include "MinimalScene.hh" + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#if ! __has_feature(objc_arc) +#error "ARC is off" +#endif + +///////////////////////////////////////////////// +namespace ignition +{ +namespace gui +{ +namespace plugins +{ + class IgnCameraTextureRhiMetalPrivate + { + public: id metalTexture = nil; + }; + + class RenderThreadRhiMetalPrivate + { + public: IgnRenderer *renderer = nullptr; + public: void *texturePtr = nullptr; + }; + + class TextureNodeRhiMetalPrivate + { + public: id metalTexture = nil; + public: id newMetalTexture = nil; + public: QSize size {0, 0}; + public: QSize newSize {0, 0}; + public: QMutex mutex; + public: QSGTexture *texture = nullptr; + public: QQuickWindow *window = nullptr; + }; +} +} +} + +using namespace ignition; +using namespace gui; +using namespace plugins; + +///////////////////////////////////////////////// +IgnCameraTextureRhiMetal::~IgnCameraTextureRhiMetal() = default; + +///////////////////////////////////////////////// +IgnCameraTextureRhiMetal::IgnCameraTextureRhiMetal() + : dataPtr(std::make_unique()) +{ +} + +///////////////////////////////////////////////// +void IgnCameraTextureRhiMetal::Update(rendering::CameraPtr _camera) +{ + void *texturePtr = nullptr; + _camera->RenderTextureMetalId(&texturePtr); + this->dataPtr->metalTexture = CFBridgingRelease(texturePtr); +} + +///////////////////////////////////////////////// +void IgnCameraTextureRhiMetal::TextureId(void* _texturePtr) +{ + *static_cast(_texturePtr) = + (void*)CFBridgingRetain(this->dataPtr->metalTexture); +} + +///////////////////////////////////////////////// +///////////////////////////////////////////////// +RenderThreadRhiMetal::~RenderThreadRhiMetal() = default; + +///////////////////////////////////////////////// +RenderThreadRhiMetal::RenderThreadRhiMetal(IgnRenderer *_renderer) + : dataPtr(std::make_unique()) +{ + this->dataPtr->renderer = _renderer; +} + +///////////////////////////////////////////////// +void RenderThreadRhiMetal::Initialize() +{ + this->dataPtr->renderer->Initialize(); +} + +///////////////////////////////////////////////// +void RenderThreadRhiMetal::RenderNext(RenderSync *_renderSync) +{ + if (!this->dataPtr->renderer->initialized) + { + this->dataPtr->renderer->Initialize(); + } + + // Check if engine has been successfully initialized + if (!this->dataPtr->renderer->initialized) + { + ignerr << "Unable to initialize renderer" << std::endl; + return; + } + + // Call the renderer + this->dataPtr->renderer->Render(_renderSync); + + // Get reference to the rendered texture + this->dataPtr->texturePtr = nullptr; + this->dataPtr->renderer->TextureId(&this->dataPtr->texturePtr); +} + +///////////////////////////////////////////////// +void* RenderThreadRhiMetal::TexturePtr() const +{ + return this->dataPtr->texturePtr; +} + +///////////////////////////////////////////////// +QSize RenderThreadRhiMetal::TextureSize() const +{ + return this->dataPtr->renderer->textureSize; +} + +///////////////////////////////////////////////// +void RenderThreadRhiMetal::ShutDown() +{ + this->dataPtr->renderer->Destroy(); + + this->dataPtr->texturePtr = nullptr; +} + +///////////////////////////////////////////////// +///////////////////////////////////////////////// +TextureNodeRhiMetal::~TextureNodeRhiMetal() +{ + delete this->dataPtr->texture; + this->dataPtr->texture = nullptr; +} + +///////////////////////////////////////////////// +TextureNodeRhiMetal::TextureNodeRhiMetal(QQuickWindow *_window) + : dataPtr(std::make_unique()) +{ + this->dataPtr->window = _window; + + // Our texture node must have a texture, so use the default 0 texture. + this->dataPtr->texture = + this->dataPtr->window->createTextureFromNativeObject( + QQuickWindow::NativeObjectTexture, + static_cast(&this->dataPtr->metalTexture), + 0, + QSize(1, 1)); +} + +///////////////////////////////////////////////// +QSGTexture *TextureNodeRhiMetal::Texture() const +{ + return this->dataPtr->texture; +} + +///////////////////////////////////////////////// +bool TextureNodeRhiMetal::HasNewTexture() const +{ + return (this->dataPtr->newMetalTexture != nil); +} + +///////////////////////////////////////////////// +void TextureNodeRhiMetal::NewTexture( + void* _texturePtr, const QSize &_size) +{ + this->dataPtr->mutex.lock(); + this->dataPtr->metalTexture = CFBridgingRelease(_texturePtr); + this->dataPtr->size = _size; + this->dataPtr->mutex.unlock(); +} + +///////////////////////////////////////////////// +void TextureNodeRhiMetal::PrepareNode() +{ + this->dataPtr->mutex.lock(); + this->dataPtr->newMetalTexture = this->dataPtr->metalTexture; + this->dataPtr->newSize = this->dataPtr->size; + this->dataPtr->metalTexture = nil; + this->dataPtr->mutex.unlock(); + + if (this->dataPtr->newMetalTexture) + { + delete this->dataPtr->texture; + this->dataPtr->texture = nullptr; + + this->dataPtr->texture = + this->dataPtr->window->createTextureFromNativeObject( + QQuickWindow::NativeObjectTexture, + static_cast(&this->dataPtr->newMetalTexture), + 0, + this->dataPtr->newSize); + } +} diff --git a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc new file mode 100644 index 000000000..e0f5ca274 --- /dev/null +++ b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include "MinimalSceneRhiOpenGL.hh" +#include "MinimalScene.hh" + +#include +#include + +#include +#include +#include +#include + +#include + +///////////////////////////////////////////////// +namespace ignition +{ +namespace gui +{ +namespace plugins +{ + class IgnCameraTextureRhiOpenGLPrivate + { + public: int textureId = 0; + }; + + class RenderThreadRhiOpenGLPrivate + { + public: IgnRenderer *renderer = nullptr; + public: void *texturePtr = nullptr; + public: QOffscreenSurface *surface = nullptr; + public: QOpenGLContext *context = nullptr; + }; + + class TextureNodeRhiOpenGLPrivate + { + public: int textureId = 0; + public: int newTextureId = 0; + public: QSize size {0, 0}; + public: QSize newSize {0, 0}; + public: QMutex mutex; + public: QSGTexture *texture = nullptr; + public: QQuickWindow *window = nullptr; + }; +} +} +} + +using namespace ignition; +using namespace gui; +using namespace plugins; + +///////////////////////////////////////////////// +IgnCameraTextureRhiOpenGL::~IgnCameraTextureRhiOpenGL() = default; + +///////////////////////////////////////////////// +IgnCameraTextureRhiOpenGL::IgnCameraTextureRhiOpenGL() + : dataPtr(std::make_unique()) +{ +} + +///////////////////////////////////////////////// +void IgnCameraTextureRhiOpenGL::Update(rendering::CameraPtr _camera) +{ + this->dataPtr->textureId = _camera->RenderTextureGLId(); +} + +///////////////////////////////////////////////// +void IgnCameraTextureRhiOpenGL::TextureId(void* _texturePtr) +{ + *reinterpret_cast(_texturePtr) = (void*)&this->dataPtr->textureId; +} + +///////////////////////////////////////////////// +///////////////////////////////////////////////// +RenderThreadRhiOpenGL::~RenderThreadRhiOpenGL() = default; + +///////////////////////////////////////////////// +RenderThreadRhiOpenGL::RenderThreadRhiOpenGL(IgnRenderer *_renderer) + : dataPtr(std::make_unique()) +{ + this->dataPtr->renderer = _renderer; +} + +///////////////////////////////////////////////// +QOffscreenSurface *RenderThreadRhiOpenGL::Surface() const +{ + return this->dataPtr->surface; +} + +///////////////////////////////////////////////// +void RenderThreadRhiOpenGL::SetSurface(QOffscreenSurface *_surface) +{ + this->dataPtr->surface = _surface; +} + +///////////////////////////////////////////////// +QOpenGLContext *RenderThreadRhiOpenGL::Context() const +{ + return this->dataPtr->context; +} + +///////////////////////////////////////////////// +void RenderThreadRhiOpenGL::SetContext(QOpenGLContext *_context) +{ + this->dataPtr->context = _context; +} + +///////////////////////////////////////////////// +void RenderThreadRhiOpenGL::Initialize() +{ + this->dataPtr->context->makeCurrent(this->dataPtr->surface); + + this->dataPtr->renderer->Initialize(); + + this->dataPtr->context->doneCurrent(); +} + +///////////////////////////////////////////////// +void RenderThreadRhiOpenGL::RenderNext(RenderSync *_renderSync) +{ + this->dataPtr->context->makeCurrent(this->dataPtr->surface); + + if (!this->dataPtr->renderer->initialized) + { + this->dataPtr->renderer->Initialize(); + } + + if (!this->dataPtr->renderer->initialized) + { + ignerr << "Unable to initialize renderer" << std::endl; + return; + } + + // Call the renderer + this->dataPtr->renderer->Render(_renderSync); + + // Get reference to the rendered texture + this->dataPtr->texturePtr = nullptr; + this->dataPtr->renderer->TextureId(&this->dataPtr->texturePtr); + + this->dataPtr->context->doneCurrent(); +} + +///////////////////////////////////////////////// +void* RenderThreadRhiOpenGL::TexturePtr() const +{ + return this->dataPtr->texturePtr; +} + +///////////////////////////////////////////////// +QSize RenderThreadRhiOpenGL::TextureSize() const +{ + return this->dataPtr->renderer->textureSize; +} + +///////////////////////////////////////////////// +void RenderThreadRhiOpenGL::ShutDown() +{ + this->dataPtr->renderer->Destroy(); + + this->dataPtr->texturePtr = nullptr; + + this->dataPtr->context->doneCurrent(); + delete this->dataPtr->context; + this->dataPtr->context = nullptr; + + // Schedule this to be deleted only after we're done cleaning up + this->dataPtr->surface->deleteLater(); +} + +///////////////////////////////////////////////// +///////////////////////////////////////////////// +TextureNodeRhiOpenGL::~TextureNodeRhiOpenGL() +{ + delete this->dataPtr->texture; + this->dataPtr->texture = nullptr; +} + +///////////////////////////////////////////////// +TextureNodeRhiOpenGL::TextureNodeRhiOpenGL(QQuickWindow *_window) + : dataPtr(std::make_unique()) +{ + this->dataPtr->window = _window; + + // Our texture node must have a texture, so use the default 0 texture. +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +# ifndef _WIN32 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# endif + this->dataPtr->texture = this->dataPtr->window->createTextureFromId( + this->dataPtr->textureId, + QSize(1, 1), + QQuickWindow::TextureIsOpaque); +# ifndef _WIN32 +# pragma GCC diagnostic pop +# endif +#else + this->dataPtr->texture = + this->dataPtr->window->createTextureFromNativeObject( + QQuickWindow::NativeObjectTexture, + static_cast(&this->dataPtr->textureId), + 0, + QSize(1, 1)); +#endif +} + +///////////////////////////////////////////////// +QSGTexture *TextureNodeRhiOpenGL::Texture() const +{ + return this->dataPtr->texture; +} + +///////////////////////////////////////////////// +bool TextureNodeRhiOpenGL::HasNewTexture() const +{ + return (this->dataPtr->newTextureId != 0); +} + +///////////////////////////////////////////////// +void TextureNodeRhiOpenGL::NewTexture( + void* _texturePtr /*[in]*/, const QSize &_size) +{ + this->dataPtr->mutex.lock(); + this->dataPtr->textureId = *static_cast(_texturePtr); + this->dataPtr->size = _size; + this->dataPtr->mutex.unlock(); +} + +///////////////////////////////////////////////// +void TextureNodeRhiOpenGL::PrepareNode() +{ + this->dataPtr->mutex.lock(); + this->dataPtr->newTextureId = this->dataPtr->textureId; + this->dataPtr->newSize = this->dataPtr->size; + this->dataPtr->textureId = 0; + this->dataPtr->mutex.unlock(); + + if (this->dataPtr->newTextureId) + { + delete this->dataPtr->texture; + this->dataPtr->texture = nullptr; + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +# ifndef _WIN32 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# endif + this->dataPtr->texture = this->dataPtr->window->createTextureFromId( + this->dataPtr->newTextureId, + this->dataPtr->newSize, + QQuickWindow::TextureIsOpaque); +# ifndef _WIN32 +# pragma GCC diagnostic pop +# endif +#else + this->dataPtr->texture = + this->dataPtr->window->createTextureFromNativeObject( + QQuickWindow::NativeObjectTexture, + static_cast(&this->dataPtr->newTextureId), + 0, + this->dataPtr->newSize); +#endif + } +} diff --git a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh new file mode 100644 index 000000000..d1302d8b9 --- /dev/null +++ b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef IGNITION_GUI_PLUGINS_MINIMALSCENE_TEXTURENODERHIOPENGL_HH_ +#define IGNITION_GUI_PLUGINS_MINIMALSCENE_TEXTURENODERHIOPENGL_HH_ + +#include "MinimalSceneRhi.hh" +#include "ignition/gui/Plugin.hh" + +#include +#include +#include + +#include + +namespace ignition +{ +namespace gui +{ +namespace plugins +{ + /// \brief Private data for IgnCameraTextureRhiOpenGL + class IgnCameraTextureRhiOpenGLPrivate; + + /// \brief Implementation of IgnCameraTextureRhi for the OpenGL graphics API + class IgnCameraTextureRhiOpenGL : public IgnCameraTextureRhi + { + // Documentation inherited + public: virtual ~IgnCameraTextureRhiOpenGL() override; + + /// \brief Constructor + public: IgnCameraTextureRhiOpenGL(); + + // Documentation inherited + public: virtual void Update(rendering::CameraPtr _camera) override; + + // Documentation inherited + public: virtual void TextureId(void* _texturePtr) override; + + /// \internal Pointer to private data + private: std::unique_ptr dataPtr; + }; + + /// \brief Private data for RenderThreadRhiOpenGLPrivate + class RenderThreadRhiOpenGLPrivate; + + /// \brief Implementation of RenderThreadRhi for the OpenGL graphics API + class RenderThreadRhiOpenGL : public RenderThreadRhi + { + // Documentation inherited + public: virtual ~RenderThreadRhiOpenGL() override; + + /// \brief Constructor + /// \param[in] _renderer The Ign-rendering renderer + public: RenderThreadRhiOpenGL(IgnRenderer *_renderer); + + // Documentation inherited + public: virtual QOffscreenSurface *Surface() const override; + + // Documentation inherited + public: virtual void SetSurface(QOffscreenSurface *_surface) override; + + // Documentation inherited + public: virtual QOpenGLContext *Context() const override; + + // Documentation inherited + public: virtual void SetContext(QOpenGLContext *_context) override; + + // Documentation inherited + public: virtual void Initialize() override; + + // Documentation inherited + public: virtual void RenderNext(RenderSync *_renderSync) override; + + // Documentation inherited + public: virtual void* TexturePtr() const override; + + // Documentation inherited + public: virtual QSize TextureSize() const override; + + // Documentation inherited + public: virtual void ShutDown() override; + + /// \internal Prevent copy and assignment + private: RenderThreadRhiOpenGL( + const RenderThreadRhiOpenGL &_other) = delete; + private: RenderThreadRhiOpenGL& operator=( + const RenderThreadRhiOpenGL &_other) = delete; + + /// \internal Pointer to private data + private: std::unique_ptr dataPtr; + }; + + /// \brief Private data for TextureNodeRhiOpenGL + class TextureNodeRhiOpenGLPrivate; + + /// \brief Implementation of TextureNodeRhi for the OpenGL graphics API + class TextureNodeRhiOpenGL : public TextureNodeRhi + { + // Documentation inherited + public: virtual ~TextureNodeRhiOpenGL() override; + + /// \brief Constructor + /// \param[in] _window Window to display the texture + public: TextureNodeRhiOpenGL(QQuickWindow *_window); + + // Documentation inherited + public: virtual QSGTexture *Texture() const override; + + // Documentation inherited + public: virtual bool HasNewTexture() const override; + + // Documentation inherited + public: virtual void NewTexture( + void* _texturePtr, const QSize &_size) override; + + // Documentation inherited + public: virtual void PrepareNode() override; + + /// \internal Prevent copy and assignment + private: TextureNodeRhiOpenGL( + const TextureNodeRhiOpenGL &_other) = delete; + private: TextureNodeRhiOpenGL& operator=( + const TextureNodeRhiOpenGL &_other) = delete; + + /// \internal Pointer to private data + private: std::unique_ptr dataPtr; + }; +} +} +} + +#endif From ec6b2393f2e2c803bbe38105b1800929079fa726 Mon Sep 17 00:00:00 2001 From: Rhys Mainwaring Date: Thu, 27 Jan 2022 00:16:09 +0000 Subject: [PATCH 2/7] [Metal] suppress cpplint warnings - Suppress cpplint warnings in MinimalSceneRhi and MinimalSceneRhiOpenGL that look like false positives Signed-off-by: Rhys Mainwaring --- src/plugins/minimal_scene/MinimalSceneRhi.cc | 4 ++-- src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/minimal_scene/MinimalSceneRhi.cc b/src/plugins/minimal_scene/MinimalSceneRhi.cc index 3026963d2..de6c2d7c0 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhi.cc +++ b/src/plugins/minimal_scene/MinimalSceneRhi.cc @@ -34,7 +34,7 @@ QOffscreenSurface *RenderThreadRhi::Surface() const } ///////////////////////////////////////////////// -void RenderThreadRhi::SetSurface(QOffscreenSurface *) +void RenderThreadRhi::SetSurface(QOffscreenSurface *) //NOLINT { /* no-op */ } @@ -46,7 +46,7 @@ QOpenGLContext *RenderThreadRhi::Context() const } ///////////////////////////////////////////////// -void RenderThreadRhi::SetContext(QOpenGLContext *) +void RenderThreadRhi::SetContext(QOpenGLContext *) //NOLINT { /* no-op */ } diff --git a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc index e0f5ca274..faf9e42e2 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc +++ b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc @@ -84,7 +84,7 @@ void IgnCameraTextureRhiOpenGL::Update(rendering::CameraPtr _camera) ///////////////////////////////////////////////// void IgnCameraTextureRhiOpenGL::TextureId(void* _texturePtr) { - *reinterpret_cast(_texturePtr) = (void*)&this->dataPtr->textureId; + *reinterpret_cast(_texturePtr) = (void*)&this->dataPtr->textureId; //NOLINT } ///////////////////////////////////////////////// From 5cef8a1b3f2550d3e380e0ed12d88e1a8c890430 Mon Sep 17 00:00:00 2001 From: Rhys Mainwaring Date: Thu, 27 Jan 2022 00:42:27 +0000 Subject: [PATCH 3/7] [Metal] ensure parameter names and documentation are consistent - Update MinimalScene.hh to fix CI error Signed-off-by: Rhys Mainwaring --- src/plugins/minimal_scene/MinimalScene.hh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/minimal_scene/MinimalScene.hh b/src/plugins/minimal_scene/MinimalScene.hh index 55ba0cad0..c51979078 100644 --- a/src/plugins/minimal_scene/MinimalScene.hh +++ b/src/plugins/minimal_scene/MinimalScene.hh @@ -113,7 +113,7 @@ namespace plugins public: void Initialize(); /// \brief Set the graphics API - /// \param[in] _graphicsApi The type of graphics API + /// \param[in] _graphicsAPI The type of graphics API public: void SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsAPI); /// \brief Destroy camera associated with this renderer @@ -281,8 +281,8 @@ namespace plugins public: void SetContext(QOpenGLContext *_context); /// \brief Set the graphics API - /// \param[in] _graphicsApi The type of graphics API - public: void SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsApi); + /// \param[in] _graphicsAPI The type of graphics API + public: void SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsAPI); /// \brief Carry out initialisation. // @@ -374,7 +374,7 @@ namespace plugins public: void SetSkyEnabled(const bool &_sky); /// \brief Set the graphics API - /// \param[in] _graphicsApi The type of graphics API + /// \param[in] _graphicsAPI The type of graphics API public: void SetGraphicsAPI(const rendering::GraphicsAPI& _graphicsAPI); /// \brief Slot called when thread is ready to be started From ca52977561c63bb615e01b25fa62b350645dfda6 Mon Sep 17 00:00:00 2001 From: Rhys Mainwaring Date: Fri, 28 Jan 2022 01:32:39 +0000 Subject: [PATCH 4/7] [Metal] incorporate feedback from review - Update include guard - Remove comment after function parameter - Document graphics_api XML element - Add error message for unsupported graphics APIs Signed-off-by: Rhys Mainwaring --- src/plugins/minimal_scene/MinimalScene.cc | 7 +++++-- src/plugins/minimal_scene/MinimalScene.hh | 1 + src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc | 2 +- src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/plugins/minimal_scene/MinimalScene.cc b/src/plugins/minimal_scene/MinimalScene.cc index 69c8725e5..91afb6299 100644 --- a/src/plugins/minimal_scene/MinimalScene.cc +++ b/src/plugins/minimal_scene/MinimalScene.cc @@ -596,7 +596,7 @@ void IgnRenderer::SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsAPI) if (_graphicsAPI == rendering::GraphicsAPI::OPENGL) { - qDebug().nospace() << "Creating ign-renderering interface for OpenGL"; + qDebug().nospace() << "Creating ign-rendering interface for OpenGL"; this->dataPtr->rhiParams["useCurrentGLContext"] = "1"; this->dataPtr->rhi = std::make_unique(); } @@ -977,7 +977,10 @@ QSGNode *RenderWindowItem::updatePaintNode(QSGNode *_node, } else { - // invalid render system + ignerr << "GraphicsAPI [" + << rendering::GraphicsAPIUtils::Str(this->dataPtr->graphicsAPI) + << "] is not supported" + << std::endl; } return nullptr; } diff --git a/src/plugins/minimal_scene/MinimalScene.hh b/src/plugins/minimal_scene/MinimalScene.hh index c51979078..885ce5484 100644 --- a/src/plugins/minimal_scene/MinimalScene.hh +++ b/src/plugins/minimal_scene/MinimalScene.hh @@ -59,6 +59,7 @@ namespace plugins /// * \ : Camera's near clipping plane distance, defaults to 0.01 /// * \ : Camera's far clipping plane distance, defaults to 1000.0 /// * \ : If present, sky is enabled. + /// * \ : Optional graphics API name, defaults to 'opengl'. class MinimalScene : public Plugin { Q_OBJECT diff --git a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc index faf9e42e2..73c0a077e 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc +++ b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc @@ -236,7 +236,7 @@ bool TextureNodeRhiOpenGL::HasNewTexture() const ///////////////////////////////////////////////// void TextureNodeRhiOpenGL::NewTexture( - void* _texturePtr /*[in]*/, const QSize &_size) + void* _texturePtr, const QSize &_size) { this->dataPtr->mutex.lock(); this->dataPtr->textureId = *static_cast(_texturePtr); diff --git a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh index d1302d8b9..3ebe4f11e 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh +++ b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh @@ -15,8 +15,8 @@ * */ -#ifndef IGNITION_GUI_PLUGINS_MINIMALSCENE_TEXTURENODERHIOPENGL_HH_ -#define IGNITION_GUI_PLUGINS_MINIMALSCENE_TEXTURENODERHIOPENGL_HH_ +#ifndef IGNITION_GUI_PLUGINS_MINIMALSCENE_MINIMALSCENERHIOPENGL_HH_ +#define IGNITION_GUI_PLUGINS_MINIMALSCENE_MINIMALSCENERHIOPENGL_HH_ #include "MinimalSceneRhi.hh" #include "ignition/gui/Plugin.hh" From 6749a92b9987f56246696b6f1ee0d4cdbcb3039c Mon Sep 17 00:00:00 2001 From: Rhys Mainwaring Date: Tue, 1 Mar 2022 13:49:24 +0000 Subject: [PATCH 5/7] [Metal] incorporate feedback from review - Add list of available graphics APIs - Set default graphics API to 'metal' if the platform is Apple, otherwise use 'opengl'. Signed-off-by: Rhys Mainwaring --- src/plugins/minimal_scene/MinimalScene.cc | 6 +++++- src/plugins/minimal_scene/MinimalScene.hh | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/minimal_scene/MinimalScene.cc b/src/plugins/minimal_scene/MinimalScene.cc index 91afb6299..adf403602 100644 --- a/src/plugins/minimal_scene/MinimalScene.cc +++ b/src/plugins/minimal_scene/MinimalScene.cc @@ -198,9 +198,13 @@ class ignition::gui::plugins::RenderWindowItem::Implementation /// \brief True if initializing (started but not complete) public: bool initializing = false; - /// \brief Graphics API + /// \brief Graphics API. The default is platform specific. public: ignition::rendering::GraphicsAPI graphicsAPI = +#ifdef __APPLE__ + rendering::GraphicsAPI::METAL; +#else rendering::GraphicsAPI::OPENGL; +#endif /// \brief Render thread public: RenderThread *renderThread = nullptr; diff --git a/src/plugins/minimal_scene/MinimalScene.hh b/src/plugins/minimal_scene/MinimalScene.hh index 885ce5484..dbdeb2434 100644 --- a/src/plugins/minimal_scene/MinimalScene.hh +++ b/src/plugins/minimal_scene/MinimalScene.hh @@ -59,7 +59,8 @@ namespace plugins /// * \ : Camera's near clipping plane distance, defaults to 0.01 /// * \ : Camera's far clipping plane distance, defaults to 1000.0 /// * \ : If present, sky is enabled. - /// * \ : Optional graphics API name, defaults to 'opengl'. + /// * \ : Optional graphics API name. Valid choices are: + /// 'opengl', 'metal'. Defaults to 'opengl'. class MinimalScene : public Plugin { Q_OBJECT From 5b0e21478423cc888c675e23bbb5709c471cd6fd Mon Sep 17 00:00:00 2001 From: Rhys Mainwaring Date: Fri, 4 Mar 2022 15:30:03 +0000 Subject: [PATCH 6/7] Update src/plugins/minimal_scene/MinimalScene.hh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alejandro Hernández Cordero Signed-off-by: Rhys Mainwaring --- src/plugins/minimal_scene/MinimalScene.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/minimal_scene/MinimalScene.hh b/src/plugins/minimal_scene/MinimalScene.hh index dbdeb2434..950d45db6 100644 --- a/src/plugins/minimal_scene/MinimalScene.hh +++ b/src/plugins/minimal_scene/MinimalScene.hh @@ -270,7 +270,6 @@ namespace plugins public: QOffscreenSurface *Surface() const; /// \brief Set the offscreen surface to render to - // /// \param[in] _surface Off-screen surface format public: void SetSurface(QOffscreenSurface *_surface); From f22261ce525268a39a732e1a973f4b13a9021f13 Mon Sep 17 00:00:00 2001 From: Rhys Mainwaring Date: Fri, 4 Mar 2022 15:37:46 +0000 Subject: [PATCH 7/7] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alejandro Hernández Cordero Signed-off-by: Rhys Mainwaring --- src/plugins/minimal_scene/MinimalScene.cc | 5 ++--- src/plugins/minimal_scene/MinimalScene.hh | 2 -- src/plugins/minimal_scene/MinimalSceneRhi.cc | 2 +- src/plugins/minimal_scene/MinimalSceneRhi.hh | 2 +- src/plugins/minimal_scene/MinimalSceneRhiMetal.hh | 2 +- src/plugins/minimal_scene/MinimalSceneRhiMetal.mm | 2 +- src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc | 3 +-- src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh | 2 +- 8 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/plugins/minimal_scene/MinimalScene.cc b/src/plugins/minimal_scene/MinimalScene.cc index adf403602..62c525219 100644 --- a/src/plugins/minimal_scene/MinimalScene.cc +++ b/src/plugins/minimal_scene/MinimalScene.cc @@ -799,8 +799,7 @@ TextureNode::TextureNode( QQuickWindow *_window, RenderSync &_renderSync, const rendering::GraphicsAPI &_graphicsAPI) - : renderSync(_renderSync) - , window(_window) + : renderSync(_renderSync) , window(_window) { if (_graphicsAPI == rendering::GraphicsAPI::OPENGL) { @@ -1259,7 +1258,7 @@ void MinimalScene::LoadConfig(const tinyxml2::XMLElement *_pluginElem) renderWindow->SetSkyEnabled(true); if (!elem->NoChildren()) ignwarn << "Child elements of are not supported yet" - << std::endl; + << std::endl; } elem = _pluginElem->FirstChildElement("graphics_api"); diff --git a/src/plugins/minimal_scene/MinimalScene.hh b/src/plugins/minimal_scene/MinimalScene.hh index 950d45db6..60c1f2a20 100644 --- a/src/plugins/minimal_scene/MinimalScene.hh +++ b/src/plugins/minimal_scene/MinimalScene.hh @@ -277,7 +277,6 @@ namespace plugins public: QOpenGLContext *Context() const; /// \brief Set the OpenGL context to be passed to the render engine - // /// \param[in] _context OpenGL context public: void SetContext(QOpenGLContext *_context); @@ -286,7 +285,6 @@ namespace plugins public: void SetGraphicsAPI(const rendering::GraphicsAPI &_graphicsAPI); /// \brief Carry out initialisation. - // /// On macOS this must be run on the main thread public: void Initialize(); diff --git a/src/plugins/minimal_scene/MinimalSceneRhi.cc b/src/plugins/minimal_scene/MinimalSceneRhi.cc index de6c2d7c0..1fa64a41f 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhi.cc +++ b/src/plugins/minimal_scene/MinimalSceneRhi.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2022 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/plugins/minimal_scene/MinimalSceneRhi.hh b/src/plugins/minimal_scene/MinimalSceneRhi.hh index 1b016bc68..908b53355 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhi.hh +++ b/src/plugins/minimal_scene/MinimalSceneRhi.hh @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2022 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/plugins/minimal_scene/MinimalSceneRhiMetal.hh b/src/plugins/minimal_scene/MinimalSceneRhiMetal.hh index 81097841d..772aad6da 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhiMetal.hh +++ b/src/plugins/minimal_scene/MinimalSceneRhiMetal.hh @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2022 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/plugins/minimal_scene/MinimalSceneRhiMetal.mm b/src/plugins/minimal_scene/MinimalSceneRhiMetal.mm index 0acdd4f65..a7f753659 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhiMetal.mm +++ b/src/plugins/minimal_scene/MinimalSceneRhiMetal.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2022 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc index 73c0a077e..f955a88af 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc +++ b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2022 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -185,7 +185,6 @@ void RenderThreadRhiOpenGL::ShutDown() this->dataPtr->surface->deleteLater(); } -///////////////////////////////////////////////// ///////////////////////////////////////////////// TextureNodeRhiOpenGL::~TextureNodeRhiOpenGL() { diff --git a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh index 3ebe4f11e..57b95bd52 100644 --- a/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh +++ b/src/plugins/minimal_scene/MinimalSceneRhiOpenGL.hh @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2022 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.