1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
4 ** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the Qt3D module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "renderer_p.h"
42 
43 #include <Qt3DCore/qentity.h>
44 
45 #include <Qt3DRender/qmaterial.h>
46 #include <Qt3DRender/qmesh.h>
47 #include <Qt3DRender/qrenderpass.h>
48 #include <Qt3DRender/qshaderprogram.h>
49 #include <Qt3DRender/qtechnique.h>
50 #include <Qt3DRender/qrenderaspect.h>
51 #include <Qt3DRender/qeffect.h>
52 
53 #include <Qt3DRender/private/qsceneimporter_p.h>
54 #include <Qt3DRender/private/renderstates_p.h>
55 #include <Qt3DRender/private/cameraselectornode_p.h>
56 #include <Qt3DRender/private/framegraphvisitor_p.h>
57 #include <Qt3DRender/private/cameralens_p.h>
58 #include <Qt3DRender/private/entity_p.h>
59 #include <Qt3DRender/private/renderlogging_p.h>
60 #include <Qt3DRender/private/material_p.h>
61 #include <Qt3DRender/private/renderpassfilternode_p.h>
62 #include <Qt3DRender/private/shader_p.h>
63 #include <Qt3DRender/private/buffer_p.h>
64 #include <Qt3DRender/private/technique_p.h>
65 #include <Qt3DRender/private/renderthread_p.h>
66 #include <Qt3DRender/private/scenemanager_p.h>
67 #include <Qt3DRender/private/techniquefilternode_p.h>
68 #include <Qt3DRender/private/viewportnode_p.h>
69 #include <Qt3DRender/private/vsyncframeadvanceservice_p.h>
70 #include <Qt3DRender/private/managers_p.h>
71 #include <Qt3DRender/private/buffermanager_p.h>
72 #include <Qt3DRender/private/nodemanagers_p.h>
73 #include <Qt3DRender/private/geometryrenderermanager_p.h>
74 #include <Qt3DRender/private/techniquemanager_p.h>
75 #include <Qt3DRender/private/platformsurfacefilter_p.h>
76 #include <Qt3DRender/private/loadbufferjob_p.h>
77 #include <Qt3DRender/private/rendercapture_p.h>
78 #include <Qt3DRender/private/updatelevelofdetailjob_p.h>
79 #include <Qt3DRender/private/buffercapture_p.h>
80 #include <Qt3DRender/private/offscreensurfacehelper_p.h>
81 #include <Qt3DRender/private/subtreeenabler_p.h>
82 #include <Qt3DRender/private/qshaderprogrambuilder_p.h>
83 #include <Qt3DRender/private/qshaderprogram_p.h>
84 
85 #include <Qt3DRender/qcameralens.h>
86 #include <Qt3DCore/private/qabstractaspectjobmanager_p.h>
87 #include <Qt3DCore/private/qaspectmanager_p.h>
88 #include <Qt3DCore/private/qsysteminformationservice_p.h>
89 #include <Qt3DCore/private/qsysteminformationservice_p_p.h>
90 #include <Qt3DRender/private/resourceaccessor_p.h>
91 #include <Qt3DRender/private/renderlogging_p.h>
92 #include <Qt3DRender/private/renderstateset_p.h>
93 #include <Qt3DRender/private/setfence_p.h>
94 #include <Qt3DRender/private/stringtoint_p.h>
95 #include <Qt3DRender/private/qrenderaspect_p.h>
96 
97 #include <rhibuffer_p.h>
98 #include <rhigraphicspipeline_p.h>
99 
100 #include <rendercommand_p.h>
101 #include <renderqueue_p.h>
102 #include <renderview_p.h>
103 #include <texture_p.h>
104 #include <renderviewbuilder_p.h>
105 #include <rhiresourcemanagers_p.h>
106 #include <commandexecuter_p.h>
107 #include <submissioncontext_p.h>
108 
109 #include <QStack>
110 #include <QOffscreenSurface>
111 #include <QSurface>
112 #include <QElapsedTimer>
113 #include <QLibraryInfo>
114 #include <QMutexLocker>
115 #include <QPluginLoader>
116 #include <QDir>
117 #include <QUrl>
118 #include <QOffscreenSurface>
119 #include <QWindow>
120 #include <QThread>
121 #include <QKeyEvent>
122 #include <QMouseEvent>
123 
124 #include <QtGui/private/qopenglcontext_p.h>
125 #include <QGuiApplication>
126 
127 QT_BEGIN_NAMESPACE
128 
129 using namespace Qt3DCore;
130 
131 namespace Qt3DRender {
132 namespace Render {
133 namespace Rhi {
134 
135 namespace {
136 
137 class CachingLightGatherer : public LightGatherer
138 {
139 public:
CachingLightGatherer(RendererCache * cache)140     CachingLightGatherer(RendererCache *cache) : LightGatherer(), m_cache(cache) { }
141 
run()142     void run() override
143     {
144         LightGatherer::run();
145 
146         QMutexLocker lock(m_cache->mutex());
147         m_cache->gatheredLights = lights();
148         m_cache->environmentLight = environmentLight();
149     }
150 
151 private:
152     RendererCache *m_cache;
153 };
154 
155 class CachingRenderableEntityFilter : public RenderableEntityFilter
156 {
157 public:
CachingRenderableEntityFilter(RendererCache * cache)158     CachingRenderableEntityFilter(RendererCache *cache) : RenderableEntityFilter(), m_cache(cache)
159     {
160     }
161 
run()162     void run() override
163     {
164         RenderableEntityFilter::run();
165 
166         QVector<Entity *> selectedEntities = filteredEntities();
167         std::sort(selectedEntities.begin(), selectedEntities.end());
168 
169         QMutexLocker lock(m_cache->mutex());
170         m_cache->renderableEntities = selectedEntities;
171     }
172 
173 private:
174     RendererCache *m_cache;
175 };
176 
177 class CachingComputableEntityFilter : public ComputableEntityFilter
178 {
179 public:
CachingComputableEntityFilter(RendererCache * cache)180     CachingComputableEntityFilter(RendererCache *cache) : ComputableEntityFilter(), m_cache(cache)
181     {
182     }
183 
run()184     void run() override
185     {
186         ComputableEntityFilter::run();
187 
188         QVector<Entity *> selectedEntities = filteredEntities();
189         std::sort(selectedEntities.begin(), selectedEntities.end());
190 
191         QMutexLocker lock(m_cache->mutex());
192         m_cache->computeEntities = selectedEntities;
193     }
194 
195 private:
196     RendererCache *m_cache;
197 };
198 
locationForAttribute(Attribute * attr,RHIShader * shader)199 int locationForAttribute(Attribute *attr, RHIShader *shader) noexcept
200 {
201     const QVector<ShaderAttribute> attribInfo = shader->attributes();
202     const auto it = std::find_if(
203             attribInfo.begin(), attribInfo.end(),
204             [attr](const ShaderAttribute &sAttr) { return attr->nameId() == sAttr.m_nameId; });
205     if (it != attribInfo.end())
206         return it->m_location;
207     return -1;
208 }
209 
210 } // anonymous
211 
212 /*!
213     \internal
214 
215     Renderer shutdown procedure:
216 
217     Since the renderer relies on the surface and OpenGLContext to perform its cleanup,
218     it is shutdown when the surface is set to nullptr
219 
220     When the surface is set to nullptr this will request the RenderThread to terminate
221     and will prevent createRenderBinJobs from returning a set of jobs as there is nothing
222     more to be rendered.
223 
224     In turn, this will call shutdown which will make the OpenGL context current one last time
225     to allow cleanups requiring a call to QOpenGLContext::currentContext to execute properly.
226     At the end of that function, the GraphicsContext is set to null.
227 
228     At this point though, the QAspectThread is still running its event loop and will only stop
229     a short while after.
230  */
231 
Renderer(QRenderAspect::RenderType type)232 Renderer::Renderer(QRenderAspect::RenderType type)
233     : m_services(nullptr),
234       m_aspect(nullptr),
235       m_nodesManager(nullptr),
236       m_renderSceneRoot(nullptr),
237       m_defaultRenderStateSet(nullptr),
238       m_submissionContext(nullptr),
239       m_renderQueue(new RenderQueue()),
240       m_renderThread(type == QRenderAspect::Threaded ? new RenderThread(this) : nullptr),
241       m_vsyncFrameAdvanceService(new VSyncFrameAdvanceService(m_renderThread != nullptr)),
242       m_waitForInitializationToBeCompleted(0),
243       m_hasBeenInitializedMutex(),
244       m_exposed(0),
245       m_lastFrameCorrect(0),
246       m_glContext(nullptr),
247       m_time(0),
248       m_settings(nullptr),
249       m_updateShaderDataTransformJob(Render::UpdateShaderDataTransformJobPtr::create()),
250       m_cleanupJob(Render::FrameCleanupJobPtr::create()),
251       m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create()),
252       m_filterCompatibleTechniqueJob(FilterCompatibleTechniqueJobPtr::create()),
253       m_lightGathererJob(new CachingLightGatherer(&m_cache)),
254       m_renderableEntityFilterJob(new CachingRenderableEntityFilter(&m_cache)),
255       m_computableEntityFilterJob(new CachingComputableEntityFilter(&m_cache)),
256       m_bufferGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyBuffers(); },
257                                                      JobTypes::DirtyBufferGathering)),
__anon62ce21360402null258       m_textureGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyTextures(); },
259                                                       JobTypes::DirtyTextureGathering)),
260       m_introspectShaderJob(SynchronizerPostFramePtr::create(
__anon62ce21360502null261               [this] { reloadDirtyShaders(); },
__anon62ce21360602(Qt3DCore::QAspectManager *m) 262               [this](Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); },
263               JobTypes::DirtyShaderGathering)),
264       m_ownedContext(false),
265       m_offscreenHelper(nullptr),
266       m_RHIResourceManagers(nullptr),
267       m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this)),
268       m_shouldSwapBuffers(true)
269 {
270     std::fill_n(m_textureTransform, 4, 0.f);
271 
272     // Set renderer as running - it will wait in the context of the
273     // RenderThread for RenderViews to be submitted
274     m_running.fetchAndStoreOrdered(1);
275     if (m_renderThread)
276         m_renderThread->waitForStart();
277 
278     m_introspectShaderJob->addDependency(m_filterCompatibleTechniqueJob);
279 
280     m_filterCompatibleTechniqueJob->setRenderer(this);
281 
282     m_defaultRenderStateSet = new RenderStateSet;
283     m_defaultRenderStateSet->addState(StateVariant::createState<DepthTest>(GL_LESS));
284     m_defaultRenderStateSet->addState(StateVariant::createState<CullFace>(GL_BACK));
285     m_defaultRenderStateSet->addState(StateVariant::createState<ColorMask>(true, true, true, true));
286 }
287 
~Renderer()288 Renderer::~Renderer()
289 {
290     Q_ASSERT(m_running.fetchAndStoreOrdered(0) == 0);
291     if (m_renderThread)
292         Q_ASSERT(m_renderThread->isFinished());
293 
294     delete m_renderQueue;
295     delete m_defaultRenderStateSet;
296     delete m_RHIResourceManagers;
297 
298     if (!m_ownedContext)
299         QObject::disconnect(m_contextConnection);
300 }
301 
dumpInfo() const302 void Renderer::dumpInfo() const
303 {
304     qDebug() << Q_FUNC_INFO << "t =" << m_time;
305 
306     const ShaderManager *shaderManager = m_nodesManager->shaderManager();
307     qDebug() << "=== Shader Manager ===";
308     qDebug() << *shaderManager;
309 
310     const TextureManager *textureManager = m_nodesManager->textureManager();
311     qDebug() << "=== Texture Manager ===";
312     qDebug() << *textureManager;
313 
314     const TextureImageManager *textureImageManager = m_nodesManager->textureImageManager();
315     qDebug() << "=== Texture Image Manager ===";
316     qDebug() << *textureImageManager;
317 }
318 
api() const319 API Renderer::api() const
320 {
321     return API::OpenGL;
322 }
323 
time() const324 qint64 Renderer::time() const
325 {
326     return m_time;
327 }
328 
setTime(qint64 time)329 void Renderer::setTime(qint64 time)
330 {
331     m_time = time;
332 }
333 
setJobsInLastFrame(int jobsInLastFrame)334 void Renderer::setJobsInLastFrame(int jobsInLastFrame)
335 {
336     m_jobsInLastFrame = jobsInLastFrame;
337 }
338 
setAspect(QRenderAspect * aspect)339 void Renderer::setAspect(QRenderAspect *aspect)
340 {
341     m_aspect = aspect;
342     m_updateShaderDataTransformJob->addDependency(
343             QRenderAspectPrivate::get(aspect)->m_worldTransformJob);
344 }
345 
setNodeManagers(NodeManagers * managers)346 void Renderer::setNodeManagers(NodeManagers *managers)
347 {
348     m_nodesManager = managers;
349     m_RHIResourceManagers = new RHIResourceManagers();
350     m_scene2DResourceAccessor.reset(new ResourceAccessor(this, m_nodesManager));
351 
352     m_updateShaderDataTransformJob->setManagers(m_nodesManager);
353     m_cleanupJob->setManagers(m_nodesManager);
354     m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager());
355     m_sendBufferCaptureJob->setManagers(m_nodesManager);
356     m_lightGathererJob->setManager(m_nodesManager->renderNodesManager());
357     m_renderableEntityFilterJob->setManager(m_nodesManager->renderNodesManager());
358     m_computableEntityFilterJob->setManager(m_nodesManager->renderNodesManager());
359 }
360 
setServices(QServiceLocator * services)361 void Renderer::setServices(QServiceLocator *services)
362 {
363     m_services = services;
364 
365     m_nodesManager->sceneManager()->setDownloadService(m_services->downloadHelperService());
366 }
367 
aspect() const368 QRenderAspect *Renderer::aspect() const
369 {
370     return m_aspect;
371 }
372 
nodeManagers() const373 NodeManagers *Renderer::nodeManagers() const
374 {
375     return m_nodesManager;
376 }
377 
378 /*!
379     \internal
380 
381     Return context which can be used to share resources safely
382     with qt3d main render context.
383 */
shareContext() const384 QOpenGLContext *Renderer::shareContext() const
385 {
386     return nullptr;
387 }
388 
389 // Executed in the reloadDirtyShader job
loadShader(Shader * shader,HShader shaderHandle)390 void Renderer::loadShader(Shader *shader, HShader shaderHandle)
391 {
392     Q_UNUSED(shader);
393     if (!m_dirtyShaders.contains(shaderHandle))
394         m_dirtyShaders.push_back(shaderHandle);
395 }
396 
setOpenGLContext(QOpenGLContext * context)397 void Renderer::setOpenGLContext(QOpenGLContext *context)
398 {
399     m_glContext = context;
400 }
401 
setScreen(QScreen * scr)402 void Renderer::setScreen(QScreen *scr)
403 {
404     m_screen = scr;
405 }
406 
screen() const407 QScreen *Renderer::screen() const
408 {
409     return m_screen;
410 }
411 
accessOpenGLTexture(Qt3DCore::QNodeId nodeId,QOpenGLTexture ** texture,QMutex ** lock,bool readonly)412 bool Renderer::accessOpenGLTexture(Qt3DCore::QNodeId nodeId, QOpenGLTexture **texture,
413                                    QMutex **lock, bool readonly)
414 {
415     RHI_UNIMPLEMENTED;
416 
417     Texture *tex = m_nodesManager->textureManager()->lookupResource(nodeId);
418     if (!tex)
419         return false;
420 
421     RHITexture *glTex = m_RHIResourceManagers->rhiTextureManager()->lookupResource(tex->peerId());
422     if (!glTex)
423         return false;
424 
425     if (glTex->isDirty())
426         return false;
427 
428     if (!readonly)
429         glTex->setExternalRenderingEnabled(true);
430 
431     //    RHITexture::TextureUpdateInfo texInfo =
432     //    glTex->createOrUpdateRhiTexture(m_submissionContext.data()); *texture = texInfo.texture;
433 
434     if (!readonly)
435         *lock = glTex->externalRenderingLock();
436 
437     return true;
438 }
439 
resourceAccessor() const440 QSharedPointer<RenderBackendResourceAccessor> Renderer::resourceAccessor() const
441 {
442     return m_scene2DResourceAccessor;
443 }
444 
445 // Called in RenderThread context by the run method of RenderThread
446 // RenderThread has locked the mutex already and unlocks it when this
447 // method termintates
initialize()448 void Renderer::initialize()
449 {
450     QMutexLocker lock(&m_hasBeenInitializedMutex);
451     m_submissionContext.reset(new SubmissionContext);
452     m_submissionContext->setRenderer(this);
453 
454     // RHI initialization
455     {
456         qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer initialize";
457         m_submissionContext->initialize();
458 
459         // We need to adapt texture coordinates
460         // m_textureTransform is (a;b) in texCoord = a * texCoord + b
461         if (m_submissionContext->rhi()->isYUpInFramebuffer()) {
462             // OpenGL case - that is what we assume to be the default so we do not change
463             // anything
464             m_textureTransform[0] = 1.f;
465             m_textureTransform[1] = 1.f;
466             m_textureTransform[2] = 0.f;
467             m_textureTransform[3] = 0.f;
468         } else {
469             // Other cases : y = 1 - y
470             m_textureTransform[0] = 1.f;
471             m_textureTransform[1] = -1.f;
472             m_textureTransform[2] = 0.f;
473             m_textureTransform[3] = 1.f;
474         }
475 
476         // Awake setScenegraphRoot in case it was waiting
477         m_waitForInitializationToBeCompleted.release(1);
478 
479         // Allow the aspect manager to proceed
480         m_vsyncFrameAdvanceService->proceedToNextFrame();
481 
482         // Force initial refresh
483         markDirty(AllDirty, nullptr);
484         return;
485     }
486 }
487 
488 /*!
489  * \internal
490  *
491  * Signals for the renderer to stop rendering. If a threaded renderer is in use,
492  * the render thread will call releaseGraphicsResources() just before the thread exits.
493  * If rendering synchronously, this function will call releaseGraphicsResources().
494  */
shutdown()495 void Renderer::shutdown()
496 {
497     // Ensure we have waited to be fully initialized before trying to shut down
498     // (in case initialization is taking place at the same time)
499     QMutexLocker lock(&m_hasBeenInitializedMutex);
500 
501     qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer shutdown";
502     m_running.storeRelaxed(0);
503 
504     // We delete any renderqueue that we may not have had time to render
505     // before the surface was destroyed
506     QMutexLocker lockRenderQueue(m_renderQueue->mutex());
507     qDeleteAll(m_renderQueue->nextFrameQueue());
508     m_renderQueue->reset();
509     lockRenderQueue.unlock();
510 
511     if (!m_renderThread) {
512         releaseGraphicsResources();
513     } else {
514         // Wake up the render thread in case it is waiting for some renderviews
515         // to be ready. The isReadyToSubmit() function checks for a shutdown
516         // having been requested.
517         m_submitRenderViewsSemaphore.release(1);
518         m_renderThread->wait();
519     }
520 
521     // Destroy internal managers
522     // This needs to be done before the nodeManager is destroy
523     // as the internal resources might somehow rely on nodeManager resources
524     delete m_RHIResourceManagers;
525     m_RHIResourceManagers = nullptr;
526 }
527 
528 /*!
529     \internal
530 
531     When using a threaded renderer this function is called in the context of the
532     RenderThread to do any shutdown and cleanup that needs to be performed in the
533     thread where the OpenGL context lives.
534 
535     When using Scene3D or anything that provides a custom QOpenGLContext (not
536     owned by Qt3D) this function is called whenever the signal
537     QOpenGLContext::aboutToBeDestroyed is emitted. In that case this function
538     is called in the context of the emitter's thread.
539 */
releaseGraphicsResources()540 void Renderer::releaseGraphicsResources()
541 {
542     // We may get called twice when running inside of a Scene3D. Once when Qt Quick
543     // wants to shutdown, and again when the render aspect gets unregistered. So
544     // check that we haven't already cleaned up before going any further.
545     if (!m_submissionContext)
546         return;
547 
548     // Try to temporarily make the context current so we can free up any resources
549     QMutexLocker locker(&m_offscreenSurfaceMutex);
550     QOffscreenSurface *offscreenSurface = m_offscreenHelper->offscreenSurface();
551     if (!offscreenSurface) {
552         qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
553         // We still need to delete the submission context
554         m_submissionContext.reset(nullptr);
555         return;
556     }
557 
558     //* QOpenGLContext *context = m_submissionContext->openGLContext();
559     //* Q_ASSERT(context);
560     //*
561     //* if (context->thread() == QThread::currentThread() && context->makeCurrent(offscreenSurface))
562     //{
563     //*
564     //*     // Clean up the graphics context and any resources
565     //*     const QVector<HRHITexture> activeTexturesHandles =
566     //m_RHIResourceManagers->rhiTextureManager()->activeHandles();
567     //*     for (const HRHITexture &textureHandle : activeTexturesHandles) {
568     //*         RHITexture *tex = m_RHIResourceManagers->rhiTextureManager()->data(textureHandle);
569     //*         tex->destroy();
570     //*     }
571     //*
572     //*     // Do the same thing with buffers
573     //*     const QVector<HRHIBuffer> activeBuffers =
574     //m_RHIResourceManagers->rhiBufferManager()->activeHandles();
575     //*     for (const HRHIBuffer &bufferHandle : activeBuffers) {
576     //*         RHIBuffer *buffer = m_RHIResourceManagers->rhiBufferManager()->data(bufferHandle);
577     //*         buffer->destroy(m_submissionContext.data());
578     //*     }
579     //*
580     //*     // Do the same thing with shaders
581     //*     const QVector<RHIShader *> shaders =
582     //m_RHIResourceManagers->rhiShaderManager()->takeActiveResources();
583     //*     qDeleteAll(shaders);
584     //*
585     //*
586     //*     context->doneCurrent();
587     //* } else {
588     //*     qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
589     //* }
590     //*
591     //* if (m_ownedContext)
592     //*     delete context;
593 
594     m_submissionContext.reset(nullptr);
595 
596     qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown";
597 }
598 
setSurfaceExposed(bool exposed)599 void Renderer::setSurfaceExposed(bool exposed)
600 {
601     qCDebug(Backend) << "Window exposed: " << exposed;
602     m_exposed.fetchAndStoreOrdered(exposed);
603 }
604 
frameGraphRoot() const605 Render::FrameGraphNode *Renderer::frameGraphRoot() const
606 {
607     Q_ASSERT(m_settings);
608     if (m_nodesManager && m_nodesManager->frameGraphManager() && m_settings)
609         return m_nodesManager->frameGraphManager()->lookupNode(m_settings->activeFrameGraphID());
610     return nullptr;
611 }
612 
613 // QAspectThread context
614 // Order of execution :
615 // 1) RenderThread is created -> release 1 of m_waitForInitializationToBeCompleted when started
616 // 2) setSceneRoot waits to acquire initialization
617 // 3) submitRenderView -> check for surface
618 //    -> make surface current + create proper glHelper if needed
setSceneRoot(Entity * sgRoot)619 void Renderer::setSceneRoot(Entity *sgRoot)
620 {
621     Q_ASSERT(sgRoot);
622 
623     // If initialization hasn't been completed we must wait
624     m_waitForInitializationToBeCompleted.acquire();
625 
626     m_renderSceneRoot = sgRoot;
627     if (!m_renderSceneRoot)
628         qCWarning(Backend) << "Failed to build render scene";
629     m_renderSceneRoot->dump();
630     qCDebug(Backend) << Q_FUNC_INFO << "DUMPING SCENE";
631 
632     // Set the scene root on the jobs
633     m_cleanupJob->setRoot(m_renderSceneRoot);
634 
635     // Set all flags to dirty
636     m_dirtyBits.marked |= AbstractRenderer::AllDirty;
637 }
638 
setSettings(RenderSettings * settings)639 void Renderer::setSettings(RenderSettings *settings)
640 {
641     m_settings = settings;
642 }
643 
settings() const644 RenderSettings *Renderer::settings() const
645 {
646     return m_settings;
647 }
648 
render()649 void Renderer::render()
650 {
651     // Traversing the framegraph tree from root to lead node
652     // Allows us to define the rendering set up
653     // Camera, RenderTarget ...
654 
655     // Utimately the renderer should be a framework
656     // For the processing of the list of renderviews
657 
658     // Matrice update, bounding volumes computation ...
659     // Should be jobs
660 
661     // namespace Qt3DCore has 2 distincts node trees
662     // One scene description
663     // One framegraph description
664 
665     while (m_running.loadRelaxed() > 0) {
666         doRender();
667         // TO DO: Restore windows exposed detection
668         // Probably needs to happens some place else though
669     }
670 }
671 
672 // Either called by render if Qt3D is in charge of the RenderThread
673 // or by QRenderAspectPrivate::renderSynchronous (for Scene3D)
doRender(bool swapBuffers)674 void Renderer::doRender(bool swapBuffers)
675 {
676     Renderer::ViewSubmissionResultData submissionData;
677     bool hasCleanedQueueAndProceeded = false;
678     bool preprocessingComplete = false;
679     bool beganDrawing = false;
680 
681     // Blocking until RenderQueue is full
682     const bool canSubmit = isReadyToSubmit();
683     m_shouldSwapBuffers = swapBuffers;
684 
685     // Lock the mutex to protect access to the renderQueue while we look for its state
686     QMutexLocker locker(m_renderQueue->mutex());
687     const bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
688     const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
689 
690     bool mustCleanResources = false;
691 
692     // When using synchronous rendering (QtQuick)
693     // We are not sure that the frame queue is actually complete
694     // Since a call to render may not be synched with the completions
695     // of the RenderViewJobs
696     // In such a case we return early, waiting for a next call with
697     // the frame queue complete at this point
698 
699     // RenderQueue is complete (but that means it may be of size 0)
700     if (canSubmit && (queueIsComplete && !queueIsEmpty)) {
701         const QVector<Render::Rhi::RenderView *> renderViews = m_renderQueue->nextFrameQueue();
702         QTaskLogger submissionStatsPart1(m_services->systemInformation(),
703                                          { JobTypes::FrameSubmissionPart1, 0 },
704                                          QTaskLogger::Submission);
705         QTaskLogger submissionStatsPart2(m_services->systemInformation(),
706                                          { JobTypes::FrameSubmissionPart2, 0 },
707                                          QTaskLogger::Submission);
708 
709         QVector<RHIPassInfo> rhiPassesInfo;
710 
711         if (canRender()) {
712             QSurface *surface = nullptr;
713             for (const RenderView *rv : renderViews) {
714                 surface = rv->surface();
715                 if (surface)
716                     break;
717             }
718 
719             // In case we did not draw because e.g. there wase no swapchain,
720             // we keep the resource updates from the previous frame.
721             if (!m_submissionContext->m_currentUpdates) {
722                 m_submissionContext->m_currentUpdates =
723                         m_submissionContext->rhi()->nextResourceUpdateBatch();
724             }
725 
726             // 1) Execute commands for buffer uploads, texture updates, shader loading first
727             updateResources();
728 
729             rhiPassesInfo = prepareCommandsSubmission(renderViews);
730             // 2) Update Pipelines and copy data into commands to allow concurrent submission
731             preprocessingComplete = true;
732 
733             bool hasCommands = false;
734             for (const RenderView *rv : renderViews) {
735                 const auto &commands = rv->commands();
736                 hasCommands = std::any_of(commands.begin(), commands.end(),
737                                           [](const RenderCommand &cmd) { return cmd.isValid(); });
738                 if (hasCommands)
739                     break;
740             }
741 
742             if (hasCommands) {
743                 // Scoped to destroy surfaceLock
744                 SurfaceLocker surfaceLock(surface);
745                 const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid());
746                 if (surfaceIsValid) {
747                     beganDrawing = m_submissionContext->beginDrawing(surface);
748                     if (beganDrawing) {
749                         // Purge shader which aren't used any longer
750                         static int callCount = 0;
751                         ++callCount;
752                         const int shaderPurgePeriod = 600;
753                         if (callCount % shaderPurgePeriod == 0)
754                             m_RHIResourceManagers->rhiShaderManager()->purge();
755                     }
756                 }
757             }
758             // 2) Proceed to next frame and start preparing frame n + 1
759             m_renderQueue->reset();
760             locker.unlock(); // Done protecting RenderQueue
761             m_vsyncFrameAdvanceService->proceedToNextFrame();
762             hasCleanedQueueAndProceeded = true;
763 
764             // Only try to submit the RenderViews if the preprocessing was successful
765             // This part of the submission is happening in parallel to the RV building for the next
766             // frame
767             if (beganDrawing) {
768                 submissionStatsPart1.end(submissionStatsPart2.restart());
769 
770                 // 3) Submit the render commands for frame n (making sure we never reference
771                 // something that could be changing) Render using current device state and renderer
772                 // configuration
773                 submissionData = submitRenderViews(rhiPassesInfo);
774 
775                 // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader
776                 // deleted...)
777                 mustCleanResources = true;
778             }
779         }
780 
781         // Execute the pending shell commands
782         m_commandExecuter->performAsynchronousCommandExecution(renderViews);
783 
784         // Delete all the RenderViews which will clear the allocators
785         // that were used for their allocation
786         qDeleteAll(renderViews);
787     }
788 
789     // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong
790     // with the rendering and/or the renderqueue is incomplete from some reason
791     // or alternatively it could be complete but empty (RenderQueue of size 0)
792 
793     if (!hasCleanedQueueAndProceeded) {
794         // RenderQueue was full but something bad happened when
795         // trying to render it and therefore proceedToNextFrame was not called
796         // Note: in this case the renderQueue mutex is still locked
797 
798         // Reset the m_renderQueue so that we won't try to render
799         // with a queue used by a previous frame with corrupted content
800         // if the current queue was correctly submitted
801         m_renderQueue->reset();
802 
803         // We allow the RenderTickClock service to proceed to the next frame
804         // In turn this will allow the aspect manager to request a new set of jobs
805         // to be performed for each aspect
806         m_vsyncFrameAdvanceService->proceedToNextFrame();
807     }
808 
809     // Perform the last swapBuffers calls after the proceedToNextFrame
810     // as this allows us to gain a bit of time for the preparation of the
811     // next frame
812     // Finish up with last surface used in the list of RenderViews
813     if (beganDrawing) {
814         SurfaceLocker surfaceLock(submissionData.surface);
815         // Finish up with last surface used in the list of RenderViews
816         const bool swapBuffers = submissionData.lastBoundFBOId == m_submissionContext->defaultFBO()
817                 && surfaceLock.isSurfaceValid() && m_shouldSwapBuffers;
818         m_submissionContext->endDrawing(swapBuffers);
819 
820         if (mustCleanResources)
821             cleanGraphicsResources();
822     }
823 }
824 
825 // Called by RenderViewJobs
826 // When the frameQueue is complete and we are using a renderThread
827 // we allow the render thread to proceed
enqueueRenderView(RenderView * renderView,int submitOrder)828 void Renderer::enqueueRenderView(RenderView *renderView, int submitOrder)
829 {
830     QMutexLocker locker(m_renderQueue->mutex()); // Prevent out of order execution
831     // We cannot use a lock free primitive here because:
832     // - QVector is not thread safe
833     // - Even if the insert is made correctly, the isFrameComplete call
834     //   could be invalid since depending on the order of execution
835     //   the counter could be complete but the renderview not yet added to the
836     //   buffer depending on whichever order the cpu decides to process this
837     const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder);
838     locker.unlock(); // We're done protecting the queue at this point
839     if (isQueueComplete) {
840         if (m_renderThread && m_running.loadRelaxed())
841             Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0);
842         m_submitRenderViewsSemaphore.release(1);
843     }
844 }
845 
canRender() const846 bool Renderer::canRender() const
847 
848 {
849     // Make sure that we've not been told to terminate
850     if (m_renderThread && !m_running.loadRelaxed()) {
851         qCDebug(Rendering) << "RenderThread termination requested whilst waiting";
852         return false;
853     }
854 
855     // TO DO: Check if all surfaces have been destroyed...
856     // It may be better if the last window to be closed trigger a call to shutdown
857     // Rather than having checks for the surface everywhere
858 
859     return true;
860 }
861 
isReadyToSubmit()862 bool Renderer::isReadyToSubmit()
863 {
864     // Make sure that we've been told to render before rendering
865     // Prevent ouf of order execution
866     m_submitRenderViewsSemaphore.acquire(1);
867 
868     // Check if shutdown has been requested
869     if (m_running.loadRelaxed() == 0)
870         return false;
871 
872     // The semaphore should only
873     // be released when the frame queue is complete and there's
874     // something to render
875     // The case of shutdown should have been handled just before
876     Q_ASSERT(m_renderQueue->isFrameQueueComplete());
877     return true;
878 }
879 
880 // Main thread
executeCommand(const QStringList & args)881 QVariant Renderer::executeCommand(const QStringList &args)
882 {
883     return m_commandExecuter->executeCommand(args);
884 }
885 
886 /*!
887     \internal
888     Called in the context of the aspect thread from QRenderAspect::onRegistered
889 */
setOffscreenSurfaceHelper(OffscreenSurfaceHelper * helper)890 void Renderer::setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper)
891 {
892     QMutexLocker locker(&m_offscreenSurfaceMutex);
893     m_offscreenHelper = helper;
894 }
895 
format()896 QSurfaceFormat Renderer::format()
897 {
898     return m_submissionContext->format();
899 }
900 
updateGraphicsPipeline(RenderCommand & cmd,RenderView * rv,int renderViewIndex)901 void Renderer::updateGraphicsPipeline(RenderCommand &cmd, RenderView *rv, int renderViewIndex)
902 {
903     if (!cmd.m_rhiShader) {
904         qDebug() << "Warning: command has no shader";
905         return;
906     }
907 
908     // The Graphics Pipeline defines
909     // - Render State (Depth, Culling, Stencil, Blending)
910     // - Shader Resources Binding
911     // - Shader Vertex Attribute Layout
912 
913     // This means we need to have one GraphicsPipeline per
914     // - geometry
915     // - material
916     // - state (RV + RC)
917 
918     RenderStateSet *renderState = nullptr;
919     {
920         RenderStateSet *globalState =
921                 (rv->stateSet() != nullptr) ? rv->stateSet() : m_defaultRenderStateSet;
922 
923         // Merge global state into local state
924         RenderStateSet *localState = cmd.m_stateSet.data();
925         if (localState != nullptr) {
926             localState->merge(globalState);
927             renderState = localState;
928         } else {
929             renderState = globalState;
930         }
931     }
932 
933     // Try to retrieve existing pipeline
934     auto &pipelineManager = *m_RHIResourceManagers->rhiGraphicsPipelineManager();
935     const GraphicsPipelineIdentifier pipelineKey { cmd.m_geometry, cmd.m_shaderId,
936                                                    renderViewIndex };
937     RHIGraphicsPipeline *graphicsPipeline = pipelineManager.getOrCreateResource(pipelineKey);
938     // TO DO: Ensure we find a way to know when the state is dirty to trigger a rebuild
939 
940     if (!graphicsPipeline) {
941         qDebug() << "Warning : could not create a graphics pipeline";
942         return;
943     }
944 
945     // Increase score so that we know the pipeline was used for this frame and shouldn't be
946     // destroyed
947     graphicsPipeline->increaseScore();
948 
949     // TO DO: Set to true if geometry, shader or render state dirty
950     bool requiredRebuild = false;
951 
952     // Note: we can rebuild add/remove things from the QRhiShaderResourceBindings after having
953     // created the pipeline and rebuild it. Changes should be picked up automatically
954 
955     // Create pipeline if it doesn't exist or needs to be updated
956     if (graphicsPipeline->pipeline() == nullptr || requiredRebuild) {
957         bool ok = true;
958 
959         const SubmissionContext::SwapChainInfo *swapchain =
960                 m_submissionContext->swapChainForSurface(rv->surface());
961         if (!swapchain || !swapchain->swapChain || !swapchain->renderPassDescriptor)
962             return;
963 
964         // TO DO: Find a way to recycle those
965         // Create Per Command UBO
966         QRhiBuffer *commandUBO = m_submissionContext->rhi()->newBuffer(
967                 QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(CommandUBO));
968         QRhiBuffer *rvUBO = m_submissionContext->rhi()->newBuffer(
969                 QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(RenderViewUBO));
970         commandUBO->build();
971         rvUBO->build();
972         graphicsPipeline->setCommandUBO(commandUBO);
973         graphicsPipeline->setRenderViewUBO(rvUBO);
974 
975         QVector<QRhiShaderResourceBinding> uboBindings;
976         uboBindings << QRhiShaderResourceBinding::uniformBuffer(
977                 0,
978                 QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
979                 rvUBO)
980                     << QRhiShaderResourceBinding::uniformBuffer(
981                                1,
982                                QRhiShaderResourceBinding::VertexStage
983                                        | QRhiShaderResourceBinding::FragmentStage,
984                                commandUBO);
985 
986         // Create additional empty UBO Buffer for UBO with binding point > 1 (since we assume 0 and
987         // 1 and for Qt3D standard values)
988         const QVector<ShaderUniformBlock> uniformBlocks = cmd.m_rhiShader->uniformBlocks();
989         QHash<int, RHIGraphicsPipeline::UBOBuffer> uboBuffers;
990         for (const ShaderUniformBlock &block : uniformBlocks) {
991             if (block.m_binding > 1) {
992                 auto handle = m_RHIResourceManagers->rhiBufferManager()->allocateResource();
993                 RHIBuffer *ubo = m_RHIResourceManagers->rhiBufferManager()->data(handle);
994                 Q_ASSERT(ubo);
995                 const QByteArray rawData(block.m_size, '\0');
996                 ubo->allocate(m_submissionContext.data(), rawData, true);
997                 ok = ubo->bind(m_submissionContext.data(), RHIBuffer::UniformBuffer);
998                 uboBuffers[block.m_binding] = { handle, ubo };
999                 uboBindings << QRhiShaderResourceBinding::uniformBuffer(
1000                         block.m_binding,
1001                         QRhiShaderResourceBinding::VertexStage
1002                                 | QRhiShaderResourceBinding::FragmentStage,
1003                         ubo->rhiBuffer());
1004             }
1005         }
1006         graphicsPipeline->setUBOs(uboBuffers);
1007 
1008         // Samplers
1009         for (const auto &textureParameter : cmd.m_parameterPack.textures()) {
1010             const auto handle = m_RHIResourceManagers->rhiTextureManager()->getOrAcquireHandle(
1011                     textureParameter.nodeId);
1012             const auto textureData = m_RHIResourceManagers->rhiTextureManager()->data(handle);
1013 
1014             for (const ShaderAttribute &samplerAttribute : cmd.m_rhiShader->samplers()) {
1015                 if (samplerAttribute.m_nameId == textureParameter.glslNameId) {
1016                     const auto rhiTexture = textureData->getRhiTexture();
1017                     const auto rhiSampler = textureData->getRhiSampler();
1018                     if (rhiTexture && rhiSampler) {
1019                         uboBindings.push_back(QRhiShaderResourceBinding::sampledTexture(
1020                                 samplerAttribute.m_location,
1021                                 QRhiShaderResourceBinding::FragmentStage, rhiTexture, rhiSampler));
1022                     }
1023                 }
1024             }
1025         }
1026 
1027         QRhiShaderResourceBindings *shaderResourceBindings =
1028                 m_submissionContext->rhi()->newShaderResourceBindings();
1029         assert(shaderResourceBindings);
1030 
1031         shaderResourceBindings->setBindings(uboBindings.cbegin(), uboBindings.cend());
1032         ok = shaderResourceBindings->build();
1033         assert(ok);
1034 
1035         // Create pipeline
1036         QRhiGraphicsPipeline *pipeline = m_submissionContext->rhi()->newGraphicsPipeline();
1037         graphicsPipeline->setShaderResourceBindings(shaderResourceBindings);
1038         graphicsPipeline->setPipeline(pipeline);
1039         assert(pipeline);
1040 
1041         const QShader vertexShader = cmd.m_rhiShader->shaderStage(QShader::VertexStage);
1042         const QShader fragmentShader = cmd.m_rhiShader->shaderStage(QShader::FragmentStage);
1043 
1044         assert(vertexShader.isValid());
1045         assert(fragmentShader.isValid());
1046 
1047         pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vertexShader },
1048                                     { QRhiShaderStage::Fragment, fragmentShader } });
1049 
1050         QVarLengthArray<QRhiVertexInputBinding, 8> inputBindings;
1051         QVarLengthArray<QRhiVertexInputAttribute, 8> rhiAttributes;
1052 
1053         const auto geom = cmd.m_geometry;
1054         const auto &attributes = geom->attributes();
1055 
1056         struct BufferBinding
1057         {
1058             Qt3DCore::QNodeId bufferId;
1059             uint stride;
1060             QRhiVertexInputBinding::Classification classification;
1061             uint attributeDivisor;
1062         };
1063         QVector<BufferBinding> uniqueBindings;
1064 
1065         auto rhiAttributeType = [](Attribute *attr) {
1066             switch (attr->vertexBaseType()) {
1067             case QAttribute::Byte:
1068             case QAttribute::UnsignedByte: {
1069                 if (attr->vertexSize() == 1)
1070                     return QRhiVertexInputAttribute::UNormByte;
1071                 if (attr->vertexSize() == 2)
1072                     return QRhiVertexInputAttribute::UNormByte2;
1073                 if (attr->vertexSize() == 4)
1074                     return QRhiVertexInputAttribute::UNormByte4;
1075                 Q_FALLTHROUGH();
1076             }
1077             case QAttribute::Float: {
1078                 if (attr->vertexSize() == 1)
1079                     return QRhiVertexInputAttribute::Float;
1080                 if (attr->vertexSize() == 2)
1081                     return QRhiVertexInputAttribute::Float2;
1082                 if (attr->vertexSize() == 3)
1083                     return QRhiVertexInputAttribute::Float3;
1084                 if (attr->vertexSize() == 4)
1085                     return QRhiVertexInputAttribute::Float4;
1086                 Q_FALLTHROUGH();
1087             }
1088             default:
1089                 qWarning() << "Attribute type not handles by RHI";
1090                 Q_UNREACHABLE();
1091             }
1092         };
1093 
1094         // QRhiVertexInputBinding -> specifies the stride of an attribute, whether it's per vertex
1095         // or per instance and the instance divisor QRhiVertexInputAttribute -> specifies the format
1096         // of the attribute (offset, type), the shader location and the index of the binding
1097         // QRhiCommandBuffer::VertexInput -> binds a buffer to a binding
1098 
1099         QHash<int, int> attributeNameToBinding;
1100 
1101         for (Qt3DCore::QNodeId attribute_id : attributes) {
1102             Attribute *attrib = m_nodesManager->attributeManager()->lookupResource(attribute_id);
1103             if (attrib->attributeType() == QAttribute::VertexAttribute) {
1104                 const bool isPerInstanceAttr = attrib->divisor() != 0;
1105                 const QRhiVertexInputBinding::Classification classification = isPerInstanceAttr
1106                         ? QRhiVertexInputBinding::PerInstance
1107                         : QRhiVertexInputBinding::PerVertex;
1108                 const BufferBinding binding { attrib->bufferId(), attrib->byteStride(),
1109                                               classification,
1110                                               isPerInstanceAttr ? attrib->divisor() : 1U };
1111 
1112                 const auto it =
1113                         std::find_if(uniqueBindings.begin(), uniqueBindings.end(),
1114                                      [binding](const BufferBinding &a) {
1115                                          return binding.bufferId == a.bufferId
1116                                                  && binding.stride == a.stride
1117                                                  && binding.classification == a.classification
1118                                                  && binding.attributeDivisor == a.attributeDivisor;
1119                                      });
1120 
1121                 int bindingIndex = uniqueBindings.size();
1122                 if (it == uniqueBindings.end())
1123                     uniqueBindings.push_back(binding);
1124                 else
1125                     bindingIndex = std::distance(uniqueBindings.begin(), it);
1126 
1127                 rhiAttributes.push_back({ bindingIndex,
1128                                           locationForAttribute(attrib, cmd.m_rhiShader),
1129                                           rhiAttributeType(attrib), attrib->byteOffset() });
1130 
1131                 attributeNameToBinding.insert(attrib->nameId(), bindingIndex);
1132             }
1133         }
1134 
1135         inputBindings.resize(uniqueBindings.size());
1136         for (int i = 0, m = uniqueBindings.size(); i < m; ++i) {
1137             const BufferBinding binding = uniqueBindings.at(i);
1138             /*
1139             qDebug() << binding.bufferId
1140                      << binding.stride
1141                      << binding.classification
1142                      << binding.attributeDivisor;
1143             */
1144             inputBindings[i] = QRhiVertexInputBinding { binding.stride, binding.classification,
1145                                                         int(binding.attributeDivisor) };
1146         }
1147 
1148         QRhiVertexInputLayout inputLayout;
1149         inputLayout.setBindings(inputBindings.begin(), inputBindings.end());
1150         inputLayout.setAttributes(rhiAttributes.begin(), rhiAttributes.end());
1151 
1152         pipeline->setVertexInputLayout(inputLayout);
1153         pipeline->setShaderResourceBindings(shaderResourceBindings);
1154 
1155         pipeline->setRenderPassDescriptor(swapchain->renderPassDescriptor);
1156 
1157         graphicsPipeline->setAttributesToBindingHash(attributeNameToBinding);
1158 
1159         // Render States
1160         m_submissionContext->applyStateSet(renderState, pipeline);
1161 
1162         ok = pipeline->build();
1163         assert(ok);
1164     }
1165 
1166     // Record RHIGraphicsPipeline into command for later use
1167     if (graphicsPipeline && graphicsPipeline->pipeline())
1168         cmd.pipeline = graphicsPipeline;
1169 }
1170 
1171 // When this function is called, we must not be processing the commands for frame n+1
1172 QVector<Renderer::RHIPassInfo>
prepareCommandsSubmission(const QVector<RenderView * > & renderViews)1173 Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderViews)
1174 {
1175     // TO DO: Find a central place to initialize RHI resources
1176     const int renderViewCount = renderViews.size();
1177 
1178     // We need to have a single RHI RenderPass per RenderTarget
1179     // as creating the pass clears the buffers
1180     // 1) We need to find all adjacents RenderViews that have the same renderTarget
1181     // and submit all of these as part of the same RHI pass
1182     QVector<RHIPassInfo> rhiPassesInfo;
1183 
1184     for (int i = 0; i < renderViewCount;) {
1185         QVector<RenderView *> sameRenderTargetRVs;
1186         QVector<QRhiBuffer *> rvUbos;
1187         RenderView *refRV = renderViews.at(i);
1188         sameRenderTargetRVs.push_back(refRV);
1189 
1190         for (i = i + 1; i < renderViewCount; ++i) {
1191             RenderView *curRV = renderViews.at(i);
1192             if (refRV->renderTargetId() == curRV->renderTargetId()) {
1193                 sameRenderTargetRVs.push_back(curRV);
1194             } else
1195                 break;
1196         }
1197 
1198         RHIPassInfo bucket;
1199         bucket.rvs = std::move(sameRenderTargetRVs);
1200         bucket.surface = refRV->surface();
1201         bucket.renderTargetId = refRV->renderTargetId();
1202         bucket.attachmentPack = refRV->attachmentPack();
1203         rhiPassesInfo.push_back(bucket);
1204     }
1205 
1206     for (int i = 0; i < renderViewCount; ++i) {
1207         RenderView *rv = renderViews.at(i);
1208         QVector<RenderCommand> &commands = rv->commands();
1209         for (RenderCommand &command : commands) {
1210             // Update/Create GraphicsPipelines
1211             if (command.m_type == RenderCommand::Draw) {
1212                 Geometry *rGeometry =
1213                         m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry);
1214                 GeometryRenderer *rGeometryRenderer =
1215                         m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(
1216                                 command.m_geometryRenderer);
1217                 // By this time shaders should have been loaded
1218                 RHIShader *shader = m_RHIResourceManagers->rhiShaderManager()->lookupResource(
1219                         command.m_shaderId);
1220                 if (!shader) {
1221                     qDebug() << "Warning: could not find shader";
1222                     continue;
1223                 }
1224 
1225                 // We should never have inserted a command for which these are null
1226                 // in the first place
1227                 Q_ASSERT(rGeometry && rGeometryRenderer && shader);
1228 
1229                 // Unset dirtiness on rGeometryRenderer only
1230                 // The rGeometry may be shared by several rGeometryRenderer
1231                 // so we cannot unset its dirtiness at this point
1232                 if (rGeometryRenderer->isDirty())
1233                     rGeometryRenderer->unsetDirty();
1234 
1235                 // Prepare the ShaderParameterPack based on the active uniforms of the shader
1236                 //  shader->prepareUniforms(command.m_parameterPack);
1237 
1238                 updateGraphicsPipeline(command, rv, i);
1239 
1240             } else if (command.m_type == RenderCommand::Compute) {
1241                 RHI_UNIMPLEMENTED;
1242                 // By this time shaders have been loaded
1243                 RHIShader *shader = m_RHIResourceManagers->rhiShaderManager()->lookupResource(
1244                         command.m_shaderId);
1245                 command.m_rhiShader = shader;
1246                 Q_ASSERT(shader);
1247 
1248                 // Prepare the ShaderParameterPack based on the active uniforms of the shader
1249                 // shader->prepareUniforms(command.m_parameterPack);
1250             }
1251         }
1252     }
1253 
1254     // Unset dirtiness on Geometry and Attributes
1255     // Note: we cannot do it in the loop above as we want to be sure that all
1256     // the VAO which reference the geometry/attributes are properly updated
1257     RHI_UNIMPLEMENTED;
1258     for (Attribute *attribute : qAsConst(m_dirtyAttributes))
1259         attribute->unsetDirty();
1260     m_dirtyAttributes.clear();
1261 
1262     for (Geometry *geometry : qAsConst(m_dirtyGeometry))
1263         geometry->unsetDirty();
1264     m_dirtyGeometry.clear();
1265 
1266     return rhiPassesInfo;
1267 }
1268 
1269 // Executed in a job
lookForDirtyBuffers()1270 void Renderer::lookForDirtyBuffers()
1271 {
1272     const std::vector<HBuffer> &activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
1273     for (const HBuffer &handle : activeBufferHandles) {
1274         Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
1275         if (buffer->isDirty())
1276             m_dirtyBuffers.push_back(handle);
1277     }
1278 }
1279 
1280 // Called in prepareSubmission
lookForDownloadableBuffers()1281 void Renderer::lookForDownloadableBuffers()
1282 {
1283     m_downloadableBuffers.clear();
1284     const std::vector<HBuffer> &activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
1285     for (const HBuffer &handle : activeBufferHandles) {
1286         Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
1287         if (buffer->access() & QBuffer::Read)
1288             m_downloadableBuffers.push_back(buffer->peerId());
1289     }
1290 }
1291 
1292 // Executed in a job
lookForDirtyTextures()1293 void Renderer::lookForDirtyTextures()
1294 {
1295     // To avoid having Texture or TextureImage maintain relationships between
1296     // one another, we instead perform a lookup here to check if a texture
1297     // image has been updated to then notify textures referencing the image
1298     // that they need to be updated
1299     TextureImageManager *imageManager = m_nodesManager->textureImageManager();
1300     const std::vector<HTextureImage> &activeTextureImageHandles = imageManager->activeHandles();
1301     Qt3DCore::QNodeIdVector dirtyImageIds;
1302     for (const HTextureImage &handle : activeTextureImageHandles) {
1303         TextureImage *image = imageManager->data(handle);
1304         if (image->isDirty()) {
1305             dirtyImageIds.push_back(image->peerId());
1306             image->unsetDirty();
1307         }
1308     }
1309 
1310     TextureManager *textureManager = m_nodesManager->textureManager();
1311     const std::vector<HTexture> &activeTextureHandles = textureManager->activeHandles();
1312     for (const HTexture &handle : activeTextureHandles) {
1313         Texture *texture = textureManager->data(handle);
1314         const QNodeIdVector imageIds = texture->textureImageIds();
1315 
1316         // Does the texture reference any of the dirty texture images?
1317         for (const QNodeId imageId : imageIds) {
1318             if (dirtyImageIds.contains(imageId)) {
1319                 texture->addDirtyFlag(Texture::DirtyImageGenerators);
1320                 break;
1321             }
1322         }
1323 
1324         // Dirty meaning that something has changed on the texture
1325         // either properties, parameters, shared texture id, generator or a texture image
1326         if (texture->dirtyFlags() != Texture::NotDirty)
1327             m_dirtyTextures.push_back(handle);
1328         // Note: texture dirty flags are reset when actually updating the
1329         // textures in updateGLResources() as resetting flags here would make
1330         // us lose information about what was dirty exactly.
1331     }
1332 }
1333 
1334 // Executed in a job
reloadDirtyShaders()1335 void Renderer::reloadDirtyShaders()
1336 {
1337     Q_ASSERT(isRunning());
1338     const std::vector<HTechnique> &activeTechniques =
1339             m_nodesManager->techniqueManager()->activeHandles();
1340     const std::vector<HShaderBuilder> &activeBuilders =
1341             m_nodesManager->shaderBuilderManager()->activeHandles();
1342     for (const HTechnique &techniqueHandle : activeTechniques) {
1343         Technique *technique = m_nodesManager->techniqueManager()->data(techniqueHandle);
1344         // If api of the renderer matches the one from the technique
1345         if (technique->isCompatibleWithRenderer()) {
1346             const auto passIds = technique->renderPasses();
1347             for (const QNodeId &passId : passIds) {
1348                 RenderPass *renderPass =
1349                         m_nodesManager->renderPassManager()->lookupResource(passId);
1350                 HShader shaderHandle =
1351                         m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram());
1352                 Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle);
1353 
1354                 ShaderBuilder *shaderBuilder = nullptr;
1355                 for (const HShaderBuilder &builderHandle : activeBuilders) {
1356                     ShaderBuilder *builder =
1357                             m_nodesManager->shaderBuilderManager()->data(builderHandle);
1358                     if (builder->shaderProgramId() == shader->peerId()) {
1359                         shaderBuilder = builder;
1360                         break;
1361                     }
1362                 }
1363 
1364                 if (shaderBuilder) {
1365                     shaderBuilder->setGraphicsApi(*technique->graphicsApiFilter());
1366 
1367                     for (int i = 0; i <= QShaderProgram::Compute; i++) {
1368                         const auto shaderType = static_cast<QShaderProgram::ShaderType>(i);
1369                         if (!shaderBuilder->shaderGraph(shaderType).isValid())
1370                             continue;
1371 
1372                         if (shaderBuilder->isShaderCodeDirty(shaderType)) {
1373                             shaderBuilder->generateCode(shaderType);
1374                             m_shaderBuilderUpdates.append(shaderBuilder->takePendingUpdates());
1375                         }
1376 
1377                         const auto code = shaderBuilder->shaderCode(shaderType);
1378                         shader->setShaderCode(shaderType, code);
1379                     }
1380                 }
1381 
1382                 if (shader != nullptr && shader->isDirty())
1383                     loadShader(shader, shaderHandle);
1384             }
1385         }
1386     }
1387 }
1388 
1389 // Executed in job (in main thread when jobs are done)
sendShaderChangesToFrontend(Qt3DCore::QAspectManager * manager)1390 void Renderer::sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager)
1391 {
1392     Q_ASSERT(isRunning());
1393 
1394     // Sync Shader
1395     const std::vector<HShader> &activeShaders = m_nodesManager->shaderManager()->activeHandles();
1396     for (const HShader &handle : activeShaders) {
1397         Shader *s = m_nodesManager->shaderManager()->data(handle);
1398         if (s->requiresFrontendSync()) {
1399             QShaderProgram *frontend =
1400                     static_cast<decltype(frontend)>(manager->lookupNode(s->peerId()));
1401             QShaderProgramPrivate *dFrontend =
1402                     static_cast<decltype(dFrontend)>(QNodePrivate::get(frontend));
1403             s->unsetRequiresFrontendSync();
1404             dFrontend->setStatus(s->status());
1405             dFrontend->setLog(s->log());
1406         }
1407     }
1408 
1409     // Sync ShaderBuilder
1410     const QVector<ShaderBuilderUpdate> shaderBuilderUpdates = std::move(m_shaderBuilderUpdates);
1411     for (const ShaderBuilderUpdate &update : shaderBuilderUpdates) {
1412         QShaderProgramBuilder *builder =
1413                 static_cast<decltype(builder)>(manager->lookupNode(update.builderId));
1414         QShaderProgramBuilderPrivate *dBuilder =
1415                 static_cast<decltype(dBuilder)>(QNodePrivate::get(builder));
1416         dBuilder->setShaderCode(update.shaderCode, update.shaderType);
1417     }
1418 }
1419 
1420 // Executed in a job (in main thread when jobs are done)
sendTextureChangesToFrontend(Qt3DCore::QAspectManager * manager)1421 void Renderer::sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager)
1422 {
1423     const QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>>
1424             updateTextureProperties = std::move(m_updatedTextureProperties);
1425     for (const auto &pair : updateTextureProperties) {
1426         const Qt3DCore::QNodeIdVector targetIds = pair.second;
1427         for (const Qt3DCore::QNodeId &targetId : targetIds) {
1428             // Lookup texture
1429             Texture *t = m_nodesManager->textureManager()->lookupResource(targetId);
1430             // If backend texture is Dirty, some property has changed and the properties we are
1431             // about to send are already outdate
1432             if (t == nullptr || t->dirtyFlags() != Texture::NotDirty)
1433                 continue;
1434 
1435             QAbstractTexture *texture =
1436                     static_cast<QAbstractTexture *>(manager->lookupNode(targetId));
1437             if (!texture)
1438                 continue;
1439             const TextureProperties &properties = pair.first.properties;
1440 
1441             const bool blocked = texture->blockNotifications(true);
1442             texture->setWidth(properties.width);
1443             texture->setHeight(properties.height);
1444             texture->setDepth(properties.depth);
1445             texture->setLayers(properties.layers);
1446             texture->setFormat(properties.format);
1447             texture->blockNotifications(blocked);
1448 
1449             QAbstractTexturePrivate *dTexture =
1450                     static_cast<QAbstractTexturePrivate *>(QNodePrivate::get(texture));
1451 
1452             dTexture->setStatus(properties.status);
1453             dTexture->setHandleType(pair.first.handleType);
1454             dTexture->setHandle(pair.first.handle);
1455         }
1456     }
1457 }
1458 
1459 // Executed in a job (in main thread when jobs done)
sendDisablesToFrontend(Qt3DCore::QAspectManager * manager)1460 void Renderer::sendDisablesToFrontend(Qt3DCore::QAspectManager *manager)
1461 {
1462     // SubtreeEnabled
1463     const auto updatedDisables = std::move(m_updatedDisableSubtreeEnablers);
1464     for (const auto &nodeId : updatedDisables) {
1465         QSubtreeEnabler *frontend = static_cast<decltype(frontend)>(manager->lookupNode(nodeId));
1466         frontend->setEnabled(false);
1467     }
1468 
1469     // Compute Commands
1470     const std::vector<HComputeCommand> &activeCommands =
1471             m_nodesManager->computeJobManager()->activeHandles();
1472     for (const HComputeCommand &handle : activeCommands) {
1473         ComputeCommand *c = m_nodesManager->computeJobManager()->data(handle);
1474         if (c->hasReachedFrameCount()) {
1475             QComputeCommand *frontend =
1476                     static_cast<decltype(frontend)>(manager->lookupNode(c->peerId()));
1477             frontend->setEnabled(false);
1478             c->resetHasReachedFrameCount();
1479         }
1480     }
1481 }
1482 
1483 // Render Thread (or QtQuick RenderThread when using Scene3D)
1484 // Scene3D: When using Scene3D rendering, we can't assume that when
1485 // updateGLResources is called, the resource handles points to still existing
1486 // objects. This is because Scene3D calls doRender independently of whether all
1487 // jobs have completed or not which in turn calls proceedToNextFrame under some
1488 // conditions. Such conditions are usually met on startup to avoid deadlocks.
1489 // proceedToNextFrame triggers the syncChanges calls for the next frame, which
1490 // may contain destruction changes targeting resources. When the above
1491 // happens, this can result in the dirtyResource vectors containing handles of
1492 // objects that may already have been destroyed
updateResources()1493 void Renderer::updateResources()
1494 {
1495     {
1496         const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers);
1497         for (const HBuffer &handle : dirtyBufferHandles) {
1498             Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
1499 
1500             // Can be null when using Scene3D rendering
1501             if (buffer == nullptr)
1502                 continue;
1503 
1504             // Forces creation if it doesn't exit
1505             // Also note the binding point doesn't really matter here, we just upload data
1506             if (!m_submissionContext->hasRHIBufferForBuffer(buffer))
1507                 m_submissionContext->rhiBufferForRenderBuffer(buffer);
1508             // Update the RHIBuffer data
1509             m_submissionContext->updateBuffer(buffer);
1510             buffer->unsetDirty();
1511         }
1512     }
1513 
1514 #ifndef SHADER_LOADING_IN_COMMAND_THREAD
1515     {
1516         const QVector<HShader> dirtyShaderHandles = std::move(m_dirtyShaders);
1517         ShaderManager *shaderManager = m_nodesManager->shaderManager();
1518         for (const HShader &handle : dirtyShaderHandles) {
1519             Shader *shader = shaderManager->data(handle);
1520 
1521             // Can be null when using Scene3D rendering
1522             if (shader == nullptr)
1523                 continue;
1524 
1525             // Compile shader
1526             m_submissionContext->loadShader(shader, shaderManager,
1527                                             m_RHIResourceManagers->rhiShaderManager());
1528         }
1529     }
1530 #endif
1531 
1532     {
1533         const QVector<HTexture> activeTextureHandles = std::move(m_dirtyTextures);
1534         for (const HTexture &handle : activeTextureHandles) {
1535             Texture *texture = m_nodesManager->textureManager()->data(handle);
1536 
1537             // Can be null when using Scene3D rendering
1538             if (texture == nullptr)
1539                 continue;
1540 
1541             // Create or Update RHITexture (the RHITexture instance is created if required
1542             // and all things that can take place without a GL context are done here)
1543             updateTexture(texture);
1544         }
1545         // We want to upload textures data at this point as the SubmissionThread and
1546         // AspectThread are locked ensuring no races between Texture/TextureImage and
1547         // RHITexture
1548         if (m_submissionContext != nullptr) {
1549             RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
1550             const std::vector<HRHITexture> &glTextureHandles = rhiTextureManager->activeHandles();
1551             // Upload texture data
1552             for (const HRHITexture &glTextureHandle : glTextureHandles) {
1553                 RHI_UNIMPLEMENTED;
1554                 RHITexture *glTexture = rhiTextureManager->data(glTextureHandle);
1555 
1556                 // We create/update the actual GL texture using the GL context at this point
1557                 const RHITexture::TextureUpdateInfo info =
1558                         glTexture->createOrUpdateRhiTexture(m_submissionContext.data());
1559 
1560                 // RHITexture creation provides us width/height/format ... information
1561                 // for textures which had not initially specified these information
1562                 // (TargetAutomatic...) Gather these information and store them to be distributed by
1563                 // a change next frame
1564                 const QNodeIdVector referenceTextureIds = {
1565                     rhiTextureManager->texNodeIdForRHITexture.value(glTexture)
1566                 };
1567                 // Store properties and referenceTextureIds
1568                 if (info.wasUpdated) {
1569                     Texture::TextureUpdateInfo updateInfo;
1570                     updateInfo.properties = info.properties;
1571                     updateInfo.handleType = QAbstractTexture::OpenGLTextureId;
1572                     //                    updateInfo.handle = info.texture ?
1573                     //                    QVariant(info.texture->textureId()) : QVariant();
1574                     m_updatedTextureProperties.push_back({ updateInfo, referenceTextureIds });
1575                 }
1576             }
1577         }
1578 
1579         // Record ids of texture to cleanup while we are still blocking the aspect thread
1580         m_textureIdsToCleanup += m_nodesManager->textureManager()->takeTexturesIdsToCleanup();
1581     }
1582 
1583     // Record list of buffer that might need uploading
1584     lookForDownloadableBuffers();
1585 }
1586 
1587 // Render Thread
updateTexture(Texture * texture)1588 void Renderer::updateTexture(Texture *texture)
1589 {
1590     RHI_UNIMPLEMENTED;
1591     // Check that the current texture images are still in place, if not, do not update
1592     const bool isValid = texture->isValid(m_nodesManager->textureImageManager());
1593     if (!isValid) {
1594         qWarning() << Q_FUNC_INFO << "QTexture referencing invalid QTextureImages";
1595         return;
1596     }
1597 
1598     // All textures are unique, if you instanciate twice the exact same texture
1599     // this will create 2 identical GLTextures, no sharing will take place
1600 
1601     // Try to find the associated RHITexture for the backend Texture
1602     RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
1603     RHITexture *rhiTexture = rhiTextureManager->lookupResource(texture->peerId());
1604 
1605     // No RHITexture associated yet -> create it
1606     if (rhiTexture == nullptr) {
1607         rhiTexture = rhiTextureManager->getOrCreateResource(texture->peerId());
1608         rhiTextureManager->texNodeIdForRHITexture.insert(rhiTexture, texture->peerId());
1609     }
1610 
1611     // Update RHITexture to match Texture instance
1612     const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags();
1613     if (dirtyFlags.testFlag(Texture::DirtySharedTextureId))
1614         rhiTexture->setSharedTextureId(texture->sharedTextureId());
1615 
1616     if (dirtyFlags.testFlag(Texture::DirtyProperties))
1617         rhiTexture->setProperties(texture->properties());
1618 
1619     if (dirtyFlags.testFlag(Texture::DirtyParameters))
1620         rhiTexture->setParameters(texture->parameters());
1621 
1622     // Will make the texture requestUpload
1623     if (dirtyFlags.testFlag(Texture::DirtyImageGenerators)) {
1624         const QNodeIdVector textureImageIds = texture->textureImageIds();
1625         QVector<RHITexture::Image> images;
1626         images.reserve(textureImageIds.size());
1627         // TODO: Move this into RHITexture directly
1628         for (const QNodeId textureImageId : textureImageIds) {
1629             const TextureImage *img =
1630                     m_nodesManager->textureImageManager()->lookupResource(textureImageId);
1631             if (img == nullptr) {
1632                 qWarning() << Q_FUNC_INFO << "invalid TextureImage handle";
1633             } else {
1634                 RHITexture::Image glImg { img->dataGenerator(), img->layer(), img->mipLevel(),
1635                                           img->face() };
1636                 images.push_back(glImg);
1637             }
1638         }
1639         rhiTexture->setImages(images);
1640     }
1641 
1642     // Will make the texture requestUpload
1643     if (dirtyFlags.testFlag(Texture::DirtyDataGenerator))
1644         rhiTexture->setGenerator(texture->dataGenerator());
1645 
1646     // Will make the texture requestUpload
1647     if (dirtyFlags.testFlag(Texture::DirtyPendingDataUpdates))
1648         rhiTexture->addTextureDataUpdates(texture->takePendingTextureDataUpdates());
1649 
1650     // Unset the dirty flag on the texture
1651     texture->unsetDirty();
1652 }
1653 
1654 // Render Thread
cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId)1655 void Renderer::cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId)
1656 {
1657     RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
1658     RHITexture *glTexture = rhiTextureManager->lookupResource(cleanedUpTextureId);
1659 
1660     // Destroying the RHITexture implicitely also destroy the GL resources
1661     if (glTexture != nullptr) {
1662         rhiTextureManager->releaseResource(cleanedUpTextureId);
1663         rhiTextureManager->texNodeIdForRHITexture.remove(glTexture);
1664     }
1665 }
1666 
1667 // Render Thread
cleanupShader(const Shader * shader)1668 void Renderer::cleanupShader(const Shader *shader)
1669 {
1670     RHIShaderManager *rhiShaderManager = m_RHIResourceManagers->rhiShaderManager();
1671     RHIShader *glShader = rhiShaderManager->lookupResource(shader->peerId());
1672 
1673     if (glShader != nullptr)
1674         rhiShaderManager->abandon(glShader, shader);
1675 }
1676 
1677 // Called by SubmitRenderView
downloadGLBuffers()1678 void Renderer::downloadGLBuffers()
1679 {
1680     const QVector<Qt3DCore::QNodeId> downloadableHandles = std::move(m_downloadableBuffers);
1681     for (const Qt3DCore::QNodeId &bufferId : downloadableHandles) {
1682         BufferManager *bufferManager = m_nodesManager->bufferManager();
1683         BufferManager::ReadLocker locker(const_cast<const BufferManager *>(bufferManager));
1684         Buffer *buffer = bufferManager->lookupResource(bufferId);
1685         // Buffer could have been destroyed at this point
1686         if (!buffer)
1687             continue;
1688         // locker is protecting us from the buffer being destroy while we're looking
1689         // up its content
1690         const QByteArray content = m_submissionContext->downloadBufferContent(buffer);
1691         m_sendBufferCaptureJob->addRequest(QPair<Qt3DCore::QNodeId, QByteArray>(bufferId, content));
1692     }
1693 }
1694 
1695 // Happens in RenderThread context when all RenderViewJobs are done
1696 // Returns the id of the last bound FBO
1697 Renderer::ViewSubmissionResultData
submitRenderViews(const QVector<RHIPassInfo> & rhiPassesInfo)1698 Renderer::submitRenderViews(const QVector<RHIPassInfo> &rhiPassesInfo)
1699 {
1700     QElapsedTimer timer;
1701     quint64 queueElapsed = 0;
1702     timer.start();
1703 
1704     quint64 frameElapsed = queueElapsed;
1705     m_lastFrameCorrect.storeRelaxed(1); // everything fine until now.....
1706 
1707     qCDebug(Memory) << Q_FUNC_INFO << "rendering frame ";
1708 
1709     // We might not want to render on the default FBO
1710     uint lastBoundFBOId = 0; // m_submissionContext->boundFrameBufferObject();
1711     QSurface *surface = nullptr;
1712     QSurface *previousSurface = nullptr;
1713     QSurface *lastUsedSurface = nullptr;
1714 
1715     const int rhiPassesCount = rhiPassesInfo.size();
1716 
1717     for (int i = 0; i < rhiPassesCount; ++i) {
1718         // Initialize GraphicsContext for drawing
1719         const RHIPassInfo &rhiPassInfo = rhiPassesInfo.at(i);
1720 
1721         // Initialize Previous surface the first time we enter this loop
1722         if (i == 0) {
1723             for (const RenderView *rv : rhiPassInfo.rvs) {
1724                 previousSurface = rv->surface();
1725                 if (previousSurface)
1726                     break;
1727             }
1728         }
1729 
1730         // Check if using the same surface as the previous RHIPassInfo.
1731         // If not, we have to free up the context from the previous surface
1732         // and make the context current on the new surface
1733         surface = rhiPassInfo.surface;
1734         SurfaceLocker surfaceLock(surface);
1735 
1736         // TO DO: Make sure that the surface we are rendering too has not been unset
1737 
1738         // For now, if we do not have a surface, skip this rhipassinfo
1739         // TODO: Investigate if it's worth providing a fallback offscreen surface
1740         //       to use when surface is null. Or if we should instead expose an
1741         //       offscreensurface to Qt3D.
1742         if (!surface || !surfaceLock.isSurfaceValid()) {
1743             m_lastFrameCorrect.storeRelaxed(0);
1744             continue;
1745         }
1746 
1747         lastUsedSurface = surface;
1748         const bool surfaceHasChanged = surface != previousSurface;
1749 
1750         if (surfaceHasChanged && previousSurface) {
1751             const bool swapBuffers = lastBoundFBOId == m_submissionContext->defaultFBO()
1752                     && surfaceLock.isSurfaceValid() && m_shouldSwapBuffers;
1753             // We only call swap buffer if we are sure the previous surface is still valid
1754             m_submissionContext->endDrawing(swapBuffers);
1755         }
1756 
1757         if (surfaceHasChanged) {
1758             // If we can't make the context current on the surface, skip to the
1759             // next RenderView. We won't get the full frame but we may get something
1760             if (!m_submissionContext->beginDrawing(surface)) {
1761                 qWarning() << "Failed to make OpenGL context current on surface";
1762                 m_lastFrameCorrect.storeRelaxed(0);
1763                 continue;
1764             }
1765 
1766             previousSurface = surface;
1767             //            lastBoundFBOId = m_submissionContext->boundFrameBufferObject();
1768         }
1769 
1770         // Apply Memory Barrier if needed
1771         //        if (renderView->memoryBarrier() != QMemoryBarrier::None)
1772         //            qWarning() << "RHI Doesn't support MemoryBarrier";
1773 
1774         // Set RenderTarget ...
1775         // Activate RenderTarget
1776         {
1777             m_submissionContext->activateRenderTarget(rhiPassInfo.renderTargetId,
1778                                                       rhiPassInfo.attachmentPack, lastBoundFBOId);
1779         }
1780 
1781         // Execute the render commands
1782         if (!executeCommandsSubmission(rhiPassInfo))
1783             m_lastFrameCorrect.storeRelaxed(
1784                     0); // something went wrong; make sure to render the next frame!
1785 
1786         // executeCommandsSubmission takes care of restoring the stateset to the value
1787         // of gc->currentContext() at the moment it was called (either
1788         // renderViewStateSet or m_defaultRenderStateSet)
1789         //        if (!renderView->renderCaptureNodeId().isNull()) {
1790         //            RHI_UNIMPLEMENTED;
1791         //*            const QRenderCaptureRequest request = renderView->renderCaptureRequest();
1792         //*            const QSize size =
1793         //m_submissionContext->renderTargetSize(renderView->surfaceSize());
1794         //*            QRect rect(QPoint(0, 0), size);
1795         //*            if (!request.rect.isEmpty())
1796         //*                rect = rect.intersected(request.rect);
1797         //*            QImage image;
1798         //*            if (!rect.isEmpty()) {
1799         //*                // Bind fbo as read framebuffer
1800         //*                m_submissionContext->bindFramebuffer(m_submissionContext->activeFBO(),
1801         //GraphicsHelperInterface::FBORead);
1802         //*                image = m_submissionContext->readFramebuffer(rect);
1803         //*            } else {
1804         //*                qWarning() << "Requested capture rectangle is outside framebuffer";
1805         //*            }
1806         //*            Render::RenderCapture *renderCapture =
1807         //*
1808         //static_cast<Render::RenderCapture*>(m_nodesManager->frameGraphManager()->lookupNode(renderView->renderCaptureNodeId()));
1809         //*            renderCapture->addRenderCapture(request.captureId, image);
1810         //*            if
1811         //(!m_pendingRenderCaptureSendRequests.contains(renderView->renderCaptureNodeId()))
1812         //* m_pendingRenderCaptureSendRequests.push_back(renderView->renderCaptureNodeId());
1813         //        }
1814 
1815         //        if (renderView->isDownloadBuffersEnable())
1816         //        {
1817         //            RHI_UNIMPLEMENTED;
1818         ////*            downloadGLBuffers();
1819         //        }
1820 
1821         //        // Perform BlitFramebuffer operations
1822         //        if (renderView->hasBlitFramebufferInfo()) {
1823         //            RHI_UNIMPLEMENTED;
1824         ////*            const auto &blitFramebufferInfo = renderView->blitFrameBufferInfo();
1825         ////*            const QNodeId inputTargetId = blitFramebufferInfo.sourceRenderTargetId;
1826         ////*            const QNodeId outputTargetId =
1827         ///blitFramebufferInfo.destinationRenderTargetId;
1828         ////*            const QRect inputRect = blitFramebufferInfo.sourceRect;
1829         ////*            const QRect outputRect = blitFramebufferInfo.destinationRect;
1830         ////*            const QRenderTargetOutput::AttachmentPoint inputAttachmentPoint =
1831         ///blitFramebufferInfo.sourceAttachmentPoint;
1832         ////*            const QRenderTargetOutput::AttachmentPoint outputAttachmentPoint =
1833         ///blitFramebufferInfo.destinationAttachmentPoint;
1834         ////*            const QBlitFramebuffer::InterpolationMethod interpolationMethod =
1835         ///blitFramebufferInfo.interpolationMethod;
1836         ////*            m_submissionContext->blitFramebuffer(inputTargetId, outputTargetId,
1837         ///inputRect, outputRect, lastBoundFBOId,
1838         ////*                                                 inputAttachmentPoint,
1839         ///outputAttachmentPoint,
1840         ////*                                                 interpolationMethod);
1841         //        }
1842 
1843         frameElapsed = timer.elapsed() - frameElapsed;
1844         qCDebug(Rendering) << Q_FUNC_INFO << "Submitted RHI Passes " << i + 1 << "/"
1845                            << rhiPassesCount << "in " << frameElapsed << "ms";
1846         frameElapsed = timer.elapsed();
1847     }
1848 
1849     // Bind lastBoundFBOId back. Needed also in threaded mode.
1850     // lastBoundFBOId != m_graphicsContext->activeFBO() when the last FrameGraph leaf
1851     // node/renderView contains RenderTargetSelector/RenderTarget
1852     if (lastBoundFBOId != m_submissionContext->activeFBO()) {
1853         RHI_UNIMPLEMENTED;
1854         //         m_submissionContext->bindFramebuffer(lastBoundFBOId,
1855         //         GraphicsHelperInterface::FBOReadAndDraw);
1856     }
1857 
1858     // Reset state and call doneCurrent if the surface
1859     // is valid and was actually activated
1860     if (lastUsedSurface) {
1861         RHI_UNIMPLEMENTED;
1862         // Reset state to the default state if the last stateset is not the
1863         // defaultRenderStateSet
1864         //        if (m_submissionContext->currentStateSet() != m_defaultRenderStateSet)
1865         //            m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet);
1866     }
1867 
1868     queueElapsed = timer.elapsed() - queueElapsed;
1869     qCDebug(Rendering) << Q_FUNC_INFO << "Submission Completed in " << timer.elapsed() << "ms";
1870 
1871     // Stores the necessary information to safely perform
1872     // the last swap buffer call
1873     ViewSubmissionResultData resultData;
1874     resultData.lastBoundFBOId = lastBoundFBOId;
1875     resultData.surface = lastUsedSurface;
1876     return resultData;
1877 }
1878 
markDirty(BackendNodeDirtySet changes,BackendNode * node)1879 void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node)
1880 {
1881     Q_UNUSED(node)
1882     m_dirtyBits.marked |= changes;
1883 }
1884 
dirtyBits()1885 Renderer::BackendNodeDirtySet Renderer::dirtyBits()
1886 {
1887     return m_dirtyBits.marked;
1888 }
1889 
1890 #if defined(QT_BUILD_INTERNAL)
clearDirtyBits(BackendNodeDirtySet changes)1891 void Renderer::clearDirtyBits(BackendNodeDirtySet changes)
1892 {
1893     m_dirtyBits.remaining &= ~changes;
1894     m_dirtyBits.marked &= ~changes;
1895 }
1896 #endif
1897 
shouldRender() const1898 bool Renderer::shouldRender() const
1899 {
1900     // Only render if something changed during the last frame, or the last frame
1901     // was not rendered successfully (or render-on-demand is disabled)
1902     return (m_settings->renderPolicy() == QRenderSettings::Always || m_dirtyBits.marked != 0
1903             || m_dirtyBits.remaining != 0 || !m_lastFrameCorrect.loadRelaxed());
1904 }
1905 
skipNextFrame()1906 void Renderer::skipNextFrame()
1907 {
1908     Q_ASSERT(m_settings->renderPolicy() != QRenderSettings::Always);
1909 
1910     // make submitRenderViews() actually run
1911     m_renderQueue->setNoRender();
1912     m_submitRenderViewsSemaphore.release(1);
1913 }
1914 
jobsDone(Qt3DCore::QAspectManager * manager)1915 void Renderer::jobsDone(Qt3DCore::QAspectManager *manager)
1916 {
1917     // called in main thread once all jobs are done running
1918 
1919     // sync captured renders to frontend
1920     const QVector<Qt3DCore::QNodeId> pendingCaptureIds =
1921             std::move(m_pendingRenderCaptureSendRequests);
1922     for (const Qt3DCore::QNodeId &id : qAsConst(pendingCaptureIds)) {
1923         auto *backend = static_cast<Qt3DRender::Render::RenderCapture *>(
1924                 m_nodesManager->frameGraphManager()->lookupNode(id));
1925         backend->syncRenderCapturesToFrontend(manager);
1926     }
1927 
1928     // Do we need to notify any texture about property changes?
1929     if (m_updatedTextureProperties.size() > 0)
1930         sendTextureChangesToFrontend(manager);
1931 
1932     sendDisablesToFrontend(manager);
1933 }
1934 
setPendingEvents(const QList<QPair<QObject *,QMouseEvent>> & mouseEvents,const QList<QKeyEvent> & keyEvents)1935 void Renderer::setPendingEvents(const QList<QPair<QObject *, QMouseEvent>> &mouseEvents,
1936                                 const QList<QKeyEvent> &keyEvents)
1937 {
1938     QMutexLocker l(&m_frameEventsMutex);
1939     m_frameMouseEvents = mouseEvents;
1940     m_frameKeyEvents = keyEvents;
1941 }
1942 
1943 // Jobs we may have to run even if no rendering will happen
preRenderingJobs()1944 QVector<QAspectJobPtr> Renderer::preRenderingJobs()
1945 {
1946     if (m_sendBufferCaptureJob->hasRequests())
1947         return { m_sendBufferCaptureJob };
1948     else
1949         return {};
1950 }
1951 
1952 // Waits to be told to create jobs for the next frame
1953 // Called by QRenderAspect jobsToExecute context of QAspectThread
1954 // Returns all the jobs (and with proper dependency chain) required
1955 // for the rendering of the scene
renderBinJobs()1956 QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
1957 {
1958     QVector<QAspectJobPtr> renderBinJobs;
1959 
1960     // Remove previous dependencies
1961     m_cleanupJob->removeDependency(QWeakPointer<QAspectJob>());
1962 
1963     const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining;
1964     m_dirtyBits.marked = {};
1965     m_dirtyBits.remaining = {};
1966     BackendNodeDirtySet notCleared = {};
1967 
1968     // Add jobs
1969     if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
1970         renderBinJobs.push_back(m_updateShaderDataTransformJob);
1971     }
1972 
1973     // TO DO: Conditionally add if skeletons dirty
1974     renderBinJobs.push_back(m_cleanupJob);
1975 
1976     // Jobs to prepare RHI Resource upload
1977     if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty)
1978         renderBinJobs.push_back(m_bufferGathererJob);
1979 
1980     if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty)
1981         renderBinJobs.push_back(m_textureGathererJob);
1982 
1983     // Layer cache is dependent on layers, layer filters (hence FG structure
1984     // changes) and the enabled flag on entities
1985     const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
1986     const bool frameGraphDirty = dirtyBitsForFrame & AbstractRenderer::FrameGraphDirty;
1987     const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty;
1988     const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty || frameGraphDirty;
1989     const bool shadersDirty = dirtyBitsForFrame & AbstractRenderer::ShadersDirty;
1990     const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty;
1991     const bool lightsDirty = dirtyBitsForFrame & AbstractRenderer::LightsDirty;
1992     const bool computeableDirty = dirtyBitsForFrame & AbstractRenderer::ComputeDirty;
1993     const bool renderableDirty = dirtyBitsForFrame & AbstractRenderer::GeometryDirty;
1994     const bool materialCacheNeedsToBeRebuilt = shadersDirty || materialDirty || frameGraphDirty;
1995     const bool renderCommandsDirty =
1996             materialCacheNeedsToBeRebuilt || renderableDirty || computeableDirty;
1997 
1998     // Rebuild Entity Layers list if layers are dirty
1999 
2000     if (renderableDirty)
2001         renderBinJobs.push_back(m_renderableEntityFilterJob);
2002 
2003     if (computeableDirty)
2004         renderBinJobs.push_back(m_computableEntityFilterJob);
2005 
2006     if (lightsDirty)
2007         renderBinJobs.push_back(m_lightGathererJob);
2008 
2009     QMutexLocker lock(m_renderQueue->mutex());
2010     if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case)
2011         // Traverse the current framegraph. For each leaf node create a
2012         // RenderView and set its configuration then create a job to
2013         // populate the RenderView with a set of RenderCommands that get
2014         // their details from the RenderNodes that are visible to the
2015         // Camera selected by the framegraph configuration
2016         if (frameGraphDirty) {
2017             FrameGraphVisitor visitor(m_nodesManager->frameGraphManager());
2018             m_frameGraphLeaves = visitor.traverse(frameGraphRoot());
2019             // Remove leaf nodes that no longer exist from cache
2020             const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys();
2021             for (FrameGraphNode *leafNode : keys) {
2022                 if (!m_frameGraphLeaves.contains(leafNode))
2023                     m_cache.leafNodeCache.remove(leafNode);
2024             }
2025 
2026             // Handle single shot subtree enablers
2027             const auto subtreeEnablers = visitor.takeEnablersToDisable();
2028             for (auto *node : subtreeEnablers)
2029                 m_updatedDisableSubtreeEnablers.push_back(node->peerId());
2030         }
2031 
2032         const int fgBranchCount = m_frameGraphLeaves.size();
2033         for (int i = 0; i < fgBranchCount; ++i) {
2034             FrameGraphNode *leaf = m_frameGraphLeaves.at(i);
2035             RenderViewBuilder builder(leaf, i, this);
2036             // If we have a new RV (wasn't in the cache before, then it contains no cached data)
2037             const bool isNewRV = !m_cache.leafNodeCache.contains(leaf);
2038             builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt || isNewRV);
2039             builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt
2040                                                              || isNewRV);
2041             builder.setRenderCommandCacheNeedsToBeRebuilt(renderCommandsDirty || isNewRV);
2042 
2043             builder.prepareJobs();
2044             renderBinJobs.append(builder.buildJobHierachy());
2045         }
2046 
2047         // Set target number of RenderViews
2048         m_renderQueue->setTargetRenderViewCount(fgBranchCount);
2049     } else {
2050         // FilterLayerEntityJob is part of the RenderViewBuilder jobs and must be run later
2051         // if none of those jobs are started this frame
2052         notCleared |= AbstractRenderer::EntityEnabledDirty;
2053         notCleared |= AbstractRenderer::FrameGraphDirty;
2054         notCleared |= AbstractRenderer::LayersDirty;
2055     }
2056 
2057     if (isRunning() && m_submissionContext->isInitialized()) {
2058         if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty)
2059             renderBinJobs.push_back(m_filterCompatibleTechniqueJob);
2060         if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty)
2061             renderBinJobs.push_back(m_introspectShaderJob);
2062     } else {
2063         notCleared |= AbstractRenderer::TechniquesDirty;
2064         notCleared |= AbstractRenderer::ShadersDirty;
2065     }
2066 
2067     m_dirtyBits.remaining = dirtyBitsForFrame & notCleared;
2068 
2069     return renderBinJobs;
2070 }
2071 
frameAdvanceService() const2072 QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const
2073 {
2074     return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data());
2075 }
2076 
2077 // Called by executeCommands
performDraw(RenderCommand * command)2078 void Renderer::performDraw(RenderCommand *command)
2079 {
2080     QRhiCommandBuffer *cb = m_submissionContext->currentFrameCommandBuffer();
2081     // Indirect Draw Calls
2082     if (command->m_drawIndirect) {
2083         RHI_UNIMPLEMENTED;
2084     } else { // Direct Draw Calls
2085 
2086         // TO DO: Add glMulti Draw variants
2087         if (command->m_primitiveType == QGeometryRenderer::Patches) {
2088             RHI_UNIMPLEMENTED;
2089             //* m_submissionContext->setVerticesPerPatch(command->m_verticesPerPatch);
2090         }
2091 
2092         if (command->m_primitiveRestartEnabled) {
2093             RHI_UNIMPLEMENTED;
2094             //* m_submissionContext->enablePrimitiveRestart(command->m_restartIndexValue);
2095         }
2096 
2097         // TO DO: Add glMulti Draw variants
2098         if (command->m_drawIndexed) {
2099             cb->drawIndexed(command->m_primitiveCount, command->m_instanceCount,
2100                             command->m_indexOffset, command->m_indexAttributeByteOffset,
2101                             command->m_firstInstance);
2102         } else {
2103             cb->draw(command->m_primitiveCount, command->m_instanceCount, command->m_firstVertex,
2104                      command->m_firstInstance);
2105         }
2106     }
2107 
2108 #if defined(QT3D_RENDER_ASPECT_RHI_DEBUG)
2109     int err = m_submissionContext->openGLContext()->functions()->glGetError();
2110     if (err)
2111         qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
2112 #endif
2113 
2114     //    if (command->m_primitiveRestartEnabled)
2115     //        m_submissionContext->disablePrimitiveRestart();
2116 }
2117 
performCompute(const RenderView *,RenderCommand * command)2118 void Renderer::performCompute(const RenderView *, RenderCommand *command)
2119 {
2120     RHI_UNIMPLEMENTED;
2121     //* {
2122     //*     RHIShader *shader =
2123     //m_RHIResourceManagers->rhiShaderManager()->lookupResource(command->m_shaderId);
2124     //*     m_submissionContext->activateShader(shader);
2125     //* }
2126     //* {
2127     //*     m_submissionContext->setParameters(command->m_parameterPack);
2128     //* }
2129     //* {
2130     //*     m_submissionContext->dispatchCompute(command->m_workGroups[0],
2131     //*             command->m_workGroups[1],
2132     //*             command->m_workGroups[2]);
2133     //* }
2134     //* // HACK: Reset the compute flag to dirty
2135     //* m_dirtyBits.marked |= AbstractRenderer::ComputeDirty;
2136 
2137     //* #if defined(QT3D_RENDER_ASPECT_RHI_DEBUG)
2138     //*     int err = m_submissionContext->openGLContext()->functions()->glGetError();
2139     //*     if (err)
2140     //*         qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
2141     //* #endif
2142 }
2143 
rhiIndexFormat(QAttribute::VertexBaseType type)2144 static auto rhiIndexFormat(QAttribute::VertexBaseType type)
2145 {
2146     switch (type) {
2147     case QAttribute::VertexBaseType ::UnsignedShort:
2148         return QRhiCommandBuffer::IndexUInt16;
2149     case QAttribute::VertexBaseType ::UnsignedInt:
2150         return QRhiCommandBuffer::IndexUInt32;
2151     default:
2152         std::abort();
2153     }
2154 }
2155 
uploadBuffersForCommand(QRhiCommandBuffer * cb,const RenderView * rv,RenderCommand & command)2156 bool Renderer::uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
2157                                        RenderCommand &command)
2158 {
2159     RHIGraphicsPipeline *graphicsPipeline = command.pipeline;
2160     if (!graphicsPipeline)
2161         return true;
2162 
2163     // Create the vertex input description
2164 
2165     // Note: we have to bind the buffers here -> which will trigger the actual
2166     // upload, as this is the only place where we know about the usage type of the buffers
2167 
2168     const auto geom = command.m_geometry;
2169     const auto &attributes = geom->attributes();
2170     const QRhiVertexInputLayout layout = graphicsPipeline->pipeline()->vertexInputLayout();
2171     const int bindingAttributeCount = std::distance(layout.cbeginBindings(), layout.cendBindings());
2172     command.vertex_input.resize(bindingAttributeCount);
2173 
2174     for (Qt3DCore::QNodeId attribute_id : attributes) {
2175         // TODO isn't there a more efficient way than doing three hash lookups ?
2176         Attribute *attrib = m_nodesManager->attributeManager()->lookupResource(attribute_id);
2177         Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attrib->bufferId());
2178         RHIBuffer *hbuf =
2179                 m_RHIResourceManagers->rhiBufferManager()->lookupResource(buffer->peerId());
2180         switch (attrib->attributeType()) {
2181         case QAttribute::VertexAttribute: {
2182             hbuf->bind(&*m_submissionContext, RHIBuffer::Type::ArrayBuffer);
2183             assert(hbuf->rhiBuffer());
2184             // Find Binding for Attribute
2185             const int bindingIndex = graphicsPipeline->bindingIndexForAttribute(attrib->nameId());
2186             // We need to reference a binding, a buffer and an offset which is always = 0
2187             // as Qt3D only assumes interleaved or continuous data but not
2188             // buffer where first half of it is attribute1 and second half attribute2
2189             command.vertex_input[bindingIndex] = { hbuf->rhiBuffer(), 0 };
2190             break;
2191         }
2192         case QAttribute::IndexAttribute: {
2193             hbuf->bind(&*m_submissionContext, RHIBuffer::Type::IndexBuffer);
2194             assert(hbuf->rhiBuffer());
2195             assert(command.indexBuffer == nullptr);
2196 
2197             command.indexBuffer = hbuf->rhiBuffer();
2198             command.indexAttribute = attrib;
2199             break;
2200         }
2201         case QAttribute::DrawIndirectAttribute:
2202             RHI_UNIMPLEMENTED;
2203             break;
2204         }
2205     }
2206 
2207     for (const BlockToUBO &pack : command.m_parameterPack.uniformBuffers()) {
2208         Buffer *cpuBuffer = nodeManagers()->bufferManager()->lookupResource(pack.m_bufferID);
2209         RHIBuffer *ubo = m_submissionContext->rhiBufferForRenderBuffer(cpuBuffer);
2210         ubo->bind(&*m_submissionContext, RHIBuffer::UniformBuffer);
2211     }
2212 
2213     return true;
2214 }
2215 
2216 namespace {
printUpload(const UniformValue & value,const QShaderDescription::BlockVariable & member)2217 void printUpload(const UniformValue &value, const QShaderDescription::BlockVariable &member)
2218 {
2219     switch (member.type) {
2220     case QShaderDescription::VariableType::Int:
2221         qDebug() << "Updating" << member.name << "with int data: " << *value.constData<int>()
2222                  << " (offset: " << member.offset << ", size: " << member.size << ")";
2223         break;
2224     case QShaderDescription::VariableType::Float:
2225         qDebug() << "Updating" << member.name << "with float data: " << *value.constData<float>()
2226                  << " (offset: " << member.offset << ", size: " << member.size << ")";
2227         break;
2228     case QShaderDescription::VariableType::Vec2:
2229         qDebug() << "Updating" << member.name << "with vec2 data: " << value.constData<float>()[0]
2230                  << ", " << value.constData<float>()[1] << " (offset: " << member.offset
2231                  << ", size: " << member.size << ")";
2232         ;
2233         break;
2234     case QShaderDescription::VariableType::Vec3:
2235         qDebug() << "Updating" << member.name << "with vec3 data: " << value.constData<float>()[0]
2236                  << ", " << value.constData<float>()[1] << ", " << value.constData<float>()[2]
2237                  << " (offset: " << member.offset << ", size: " << member.size << ")";
2238         ;
2239         break;
2240     case QShaderDescription::VariableType::Vec4:
2241         qDebug() << "Updating" << member.name << "with vec4 data: " << value.constData<float>()[0]
2242                  << ", " << value.constData<float>()[1] << ", " << value.constData<float>()[2]
2243                  << ", " << value.constData<float>()[3] << " (offset: " << member.offset
2244                  << ", size: " << member.size << ")";
2245         ;
2246         break;
2247     default:
2248         qDebug() << "Updating" << member.name << "with data: " << value.constData<char>();
2249         break;
2250     }
2251 }
2252 
uploadUniform(SubmissionContext & submissionContext,const PackUniformHash & uniforms,const RHIShader::UBO_Member & uboMember,const QHash<int,RHIGraphicsPipeline::UBOBuffer> & uboBuffers,const QString & uniformName,const QShaderDescription::BlockVariable & member,int arrayOffset=0)2253 void uploadUniform(SubmissionContext &submissionContext, const PackUniformHash &uniforms,
2254                    const RHIShader::UBO_Member &uboMember,
2255                    const QHash<int, RHIGraphicsPipeline::UBOBuffer> &uboBuffers,
2256                    const QString &uniformName, const QShaderDescription::BlockVariable &member,
2257                    int arrayOffset = 0)
2258 {
2259     const int uniformNameId = StringToInt::lookupId(uniformName);
2260 
2261     if (!uniforms.contains(uniformNameId))
2262         return;
2263 
2264     const UniformValue value = uniforms.value(uniformNameId);
2265     const ShaderUniformBlock block = uboMember.block;
2266 
2267     // Update UBO with uniform value
2268     Q_ASSERT(uboBuffers.contains(block.m_binding));
2269     const RHIGraphicsPipeline::UBOBuffer &ubo = uboBuffers[block.m_binding];
2270     RHIBuffer *buffer = ubo.buffer;
2271 
2272     // TODO we should maybe have this thread_local to not reallocate memory every time
2273     QByteArray rawData;
2274     rawData.resize(member.size);
2275     memcpy(rawData.data(), value.constData<char>(), std::min(value.byteSize(), member.size));
2276     buffer->update(&submissionContext, rawData, member.offset + arrayOffset);
2277 
2278     // printUpload(value, member);
2279 }
2280 }
2281 
uploadUBOsForCommand(QRhiCommandBuffer * cb,const RenderView * rv,const RenderCommand & command)2282 bool Renderer::uploadUBOsForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
2283                                     const RenderCommand &command)
2284 {
2285     RHIGraphicsPipeline *pipeline = command.pipeline;
2286     if (!pipeline)
2287         return true;
2288 
2289     // Upload UBO data for the Command
2290     QRhiBuffer *commandUBO = pipeline->commandUBO();
2291     m_submissionContext->m_currentUpdates->updateDynamicBuffer(commandUBO, 0, sizeof(CommandUBO),
2292                                                                &command.m_commandUBO);
2293 
2294     // We have to update the RV UBO once per graphics pipeline
2295     QRhiBuffer *rvUBO = pipeline->renderViewUBO();
2296     m_submissionContext->m_currentUpdates->updateDynamicBuffer(rvUBO, 0, sizeof(RenderViewUBO),
2297                                                                rv->renderViewUBO());
2298 
2299     // Upload UBO for custom parameters
2300     {
2301         RHIShader *shader =
2302                 m_RHIResourceManagers->rhiShaderManager()->lookupResource(command.m_shaderId);
2303         if (!shader)
2304             return true;
2305 
2306         const QVector<RHIShader::UBO_Member> &uboMembers = shader->uboMembers();
2307         const QHash<int, RHIGraphicsPipeline::UBOBuffer> &uboBuffers = pipeline->ubos();
2308         const ShaderParameterPack &parameterPack = command.m_parameterPack;
2309         const PackUniformHash &uniforms = parameterPack.uniforms();
2310 
2311         // Update Buffer CPU side data based on uniforms being set
2312         for (const RHIShader::UBO_Member &uboMember : uboMembers) {
2313             for (const QShaderDescription::BlockVariable &member : qAsConst(uboMember.members)) {
2314 
2315                 if (!member.arrayDims.empty()) {
2316                     if (!member.structMembers.empty()) {
2317                         const int arr0 = member.arrayDims[0];
2318                         for (int i = 0; i < arr0; i++) {
2319                             for (const QShaderDescription::BlockVariable &structMember :
2320                                  member.structMembers) {
2321                                 const QString processedName = member.name + "[" + QString::number(i)
2322                                         + "]." + structMember.name;
2323                                 uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
2324                                               processedName, structMember, i * member.size / arr0);
2325                             }
2326                         }
2327                     } else {
2328                         uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
2329                                       member.name, member);
2330                     }
2331                 } else {
2332                     uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
2333                                   member.name, member);
2334                 }
2335             }
2336         }
2337         // Upload changes to GPU Buffer
2338         for (const RHIGraphicsPipeline::UBOBuffer &ubo : uboBuffers) {
2339             // Binding triggers the upload
2340             ubo.buffer->bind(m_submissionContext.data(), RHIBuffer::UniformBuffer);
2341         }
2342     }
2343     return true;
2344 }
2345 
performDraw(QRhiCommandBuffer * cb,const QRhiViewport & vp,const QRhiScissor * scissor,const RenderCommand & command)2346 bool Renderer::performDraw(QRhiCommandBuffer *cb, const QRhiViewport &vp,
2347                            const QRhiScissor *scissor, const RenderCommand &command)
2348 {
2349     RHIGraphicsPipeline *pipeline = command.pipeline;
2350     if (!pipeline)
2351         return true;
2352 
2353     // Setup the rendering pass
2354     cb->setGraphicsPipeline(pipeline->pipeline());
2355     cb->setViewport(vp);
2356     if (scissor)
2357         cb->setScissor(*scissor);
2358     cb->setShaderResources(pipeline->pipeline()->shaderResourceBindings());
2359 
2360     // Send the draw command
2361     if (Q_UNLIKELY(!command.indexBuffer)) {
2362         cb->setVertexInput(0, command.vertex_input.size(), command.vertex_input.data());
2363         cb->draw(command.m_primitiveCount, command.m_instanceCount, command.m_firstVertex,
2364                  command.m_firstInstance);
2365     } else {
2366         auto indexFormat = rhiIndexFormat(command.indexAttribute->vertexBaseType());
2367         auto indexOffset = command.indexAttribute->byteOffset();
2368         cb->setVertexInput(0, command.vertex_input.size(), command.vertex_input.data(),
2369                            command.indexBuffer, indexOffset, indexFormat);
2370         cb->drawIndexed(command.m_primitiveCount, command.m_instanceCount, command.m_indexOffset,
2371                         command.m_indexAttributeByteOffset, command.m_firstInstance);
2372     }
2373     return true;
2374 }
2375 
2376 // Called by RenderView->submit() in RenderThread context
2377 // Returns true, if all RenderCommands were sent to the GPU
executeCommandsSubmission(const RHIPassInfo & passInfo)2378 bool Renderer::executeCommandsSubmission(const RHIPassInfo &passInfo)
2379 {
2380     bool allCommandsIssued = true;
2381 
2382     const QVector<RenderView *> &renderViews = passInfo.rvs;
2383     QColor clearColor;
2384     QRhiDepthStencilClearValue clearDepthStencil;
2385 
2386     // Submit the commands to the underlying graphics API (RHI)
2387     QRhiCommandBuffer *cb = m_submissionContext->currentFrameCommandBuffer();
2388 
2389     // Upload data for all RenderCommands
2390     for (RenderView *rv : renderViews) {
2391         // Render drawing commands
2392 
2393         QVector<RenderCommand> &commands = rv->commands();
2394 
2395         // Upload all the required data to rhi...
2396         for (RenderCommand &command : commands) {
2397             if (command.m_type == RenderCommand::Draw) {
2398                 uploadBuffersForCommand(cb, rv, command);
2399                 uploadUBOsForCommand(cb, rv, command);
2400             }
2401         }
2402 
2403         // Record clear information
2404         if (rv->clearTypes() != QClearBuffers::None) {
2405             clearColor = [=] {
2406                 auto col = rv->globalClearColorBufferInfo().clearColor;
2407                 return QColor::fromRgbF(col.x(), col.y(), col.z(), col.w());
2408             }();
2409             clearDepthStencil = { rv->clearDepthValue(), (quint32)rv->clearStencilValue() };
2410         }
2411     }
2412     // TO DO: should be moved elsewhere
2413     // Perform compute actions
2414     //        cb->beginComputePass(m_submissionContext->m_currentUpdates);
2415     //        for (RenderCommand &command : commands) {
2416     //            if (command.m_type == RenderCommand::Compute) {
2417     //                performCompute(rv, &command);
2418     //            }
2419     //        }
2420     //        cb->endComputePass();
2421     //    m_submissionContext->m_currentUpdates =
2422     //    m_submissionContext->rhi()->nextResourceUpdateBatch();
2423 
2424     // Draw the commands
2425 
2426     // TO DO: Retrieve real renderTarget for RHIPassInfo
2427     QRhiRenderTarget *renderTarget = m_submissionContext->currentFrameRenderTarget();
2428 
2429     // Begin pass
2430     cb->beginPass(renderTarget, clearColor, clearDepthStencil,
2431                   m_submissionContext->m_currentUpdates);
2432 
2433     // Per Pass Global States
2434     for (RenderView *rv : renderViews) {
2435         // Viewport
2436         QRhiViewport vp;
2437         QRhiScissor scissor;
2438         bool hasScissor = false;
2439         {
2440             const float x = rv->viewport().x() * rv->surfaceSize().width();
2441             const float y = (1. - rv->viewport().y() - rv->viewport().height())
2442                     * rv->surfaceSize().height();
2443             const float w = rv->viewport().width() * rv->surfaceSize().width();
2444             const float h = rv->viewport().height() * rv->surfaceSize().height();
2445             //            qDebug() << x << y << w << h;
2446             vp = { x, y, w, h };
2447         }
2448         // Scissoring
2449         {
2450             RenderStateSet *ss = rv->stateSet();
2451             if (ss == nullptr)
2452                 ss = m_defaultRenderStateSet;
2453             StateVariant *scissorTestSVariant =
2454                     m_submissionContext->getState(ss, StateMask::ScissorStateMask);
2455             if (scissorTestSVariant) {
2456                 const ScissorTest *scissorTest =
2457                         static_cast<const ScissorTest *>(scissorTestSVariant->constState());
2458                 const auto &scissorValues = scissorTest->values();
2459                 scissor = { std::get<0>(scissorValues), std::get<1>(scissorValues),
2460                             std::get<2>(scissorValues), std::get<3>(scissorValues) };
2461                 hasScissor = true;
2462             }
2463         }
2464 
2465         // Render drawing commands
2466         const QVector<RenderCommand> &commands = rv->commands();
2467 
2468         for (const RenderCommand &command : commands) {
2469             if (command.m_type == RenderCommand::Draw) {
2470                 performDraw(cb, vp, hasScissor ? &scissor : nullptr, command);
2471             }
2472         }
2473     }
2474 
2475     cb->endPass();
2476     m_submissionContext->m_currentUpdates = m_submissionContext->rhi()->nextResourceUpdateBatch();
2477 
2478     return allCommandsIssued;
2479 }
2480 
2481 // Erase graphics related resources that may become unused after a frame
cleanGraphicsResources()2482 void Renderer::cleanGraphicsResources()
2483 {
2484     // Remove unused GraphicsPipeline
2485     RHIGraphicsPipelineManager *pipelineManager =
2486             m_RHIResourceManagers->rhiGraphicsPipelineManager();
2487     const std::vector<HRHIGraphicsPipeline> &graphicsPipelinesHandles = pipelineManager->activeHandles();
2488     for (HRHIGraphicsPipeline pipelineHandle : graphicsPipelinesHandles) {
2489         RHIGraphicsPipeline *pipeline = pipelineManager->data(pipelineHandle);
2490         pipeline->decreaseScore();
2491         // Pipeline wasn't used recently, let's destroy it
2492         if (pipeline->score() < 0) {
2493             pipeline->cleanup();
2494         }
2495     }
2496 
2497     // Clean buffers
2498     const QVector<Qt3DCore::QNodeId> buffersToRelease =
2499             m_nodesManager->bufferManager()->takeBuffersToRelease();
2500     for (Qt3DCore::QNodeId bufferId : buffersToRelease)
2501         m_submissionContext->releaseBuffer(bufferId);
2502 
2503     // When Textures are cleaned up, their id is saved so that they can be
2504     // cleaned up in the render thread
2505     const QVector<Qt3DCore::QNodeId> cleanedUpTextureIds = std::move(m_textureIdsToCleanup);
2506     for (const Qt3DCore::QNodeId textureCleanedUpId : cleanedUpTextureIds)
2507         cleanupTexture(textureCleanedUpId);
2508 
2509     // Abandon GL shaders when a Shader node is destroyed Note: We are sure
2510     // that when this gets executed, all scene changes have been received and
2511     // shader nodes updated
2512     const QVector<Qt3DCore::QNodeId> cleanedUpShaderIds =
2513             m_nodesManager->shaderManager()->takeShaderIdsToCleanup();
2514     for (const Qt3DCore::QNodeId shaderCleanedUpId : cleanedUpShaderIds) {
2515         cleanupShader(m_nodesManager->shaderManager()->lookupResource(shaderCleanedUpId));
2516         // We can really release the texture at this point
2517         m_nodesManager->shaderManager()->releaseResource(shaderCleanedUpId);
2518     }
2519 }
2520 
contextInfo() const2521 const GraphicsApiFilterData *Renderer::contextInfo() const
2522 {
2523     return m_submissionContext->contextInfo();
2524 }
2525 
submissionContext() const2526 SubmissionContext *Renderer::submissionContext() const
2527 {
2528     return m_submissionContext.data();
2529 }
2530 
2531 } // namespace Rhi
2532 } // namespace Render
2533 } // namespace Qt3DRender
2534 
2535 QT_END_NAMESPACE
2536