summaryrefslogtreecommitdiffstats
path: root/src/render/backend/renderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/render/backend/renderer.cpp')
-rw-r--r--src/render/backend/renderer.cpp290
1 files changed, 208 insertions, 82 deletions
diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp
index af73b1ae5..b89f21522 100644
--- a/src/render/backend/renderer.cpp
+++ b/src/render/backend/renderer.cpp
@@ -68,6 +68,7 @@
#include <Qt3DRender/private/technique_p.h>
#include <Qt3DRender/private/renderthread_p.h>
#include <Qt3DRender/private/renderview_p.h>
+#include <Qt3DRender/private/scenemanager_p.h>
#include <Qt3DRender/private/techniquefilternode_p.h>
#include <Qt3DRender/private/viewportnode_p.h>
#include <Qt3DRender/private/vsyncframeadvanceservice_p.h>
@@ -84,13 +85,19 @@
#include <Qt3DRender/private/loadbufferjob_p.h>
#include <Qt3DRender/private/rendercapture_p.h>
#include <Qt3DRender/private/updatelevelofdetailjob_p.h>
+#include <Qt3DRender/private/buffercapture_p.h>
#include <Qt3DRender/private/offscreensurfacehelper_p.h>
+#include <Qt3DRender/private/renderviewbuilder_p.h>
#include <Qt3DRender/qcameralens.h>
+#include <Qt3DCore/qt3dcore-config.h>
#include <Qt3DCore/private/qeventfilterservice_p.h>
#include <Qt3DCore/private/qabstractaspectjobmanager_p.h>
#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
+
+#if defined(QT3D_JOBS_RUN_STATS)
#include <Qt3DCore/private/aspectcommanddebugger_p.h>
+#endif
#include <QStack>
#include <QOffscreenSurface>
@@ -146,6 +153,7 @@ namespace Render {
Renderer::Renderer(QRenderAspect::RenderType type)
: m_services(nullptr)
, m_nodesManager(nullptr)
+ , m_renderSceneRoot(nullptr)
, m_defaultRenderStateSet(nullptr)
, m_graphicsContext(nullptr)
, m_renderQueue(new RenderQueue())
@@ -157,6 +165,7 @@ Renderer::Renderer(QRenderAspect::RenderType type)
, m_changeSet(0)
, m_lastFrameCorrect(0)
, m_glContext(nullptr)
+ , m_shareContext(nullptr)
, m_pickBoundingVolumeJob(PickBoundingVolumeJobPtr::create())
, m_time(0)
, m_settings(nullptr)
@@ -168,10 +177,12 @@ Renderer::Renderer(QRenderAspect::RenderType type)
, m_updateWorldBoundingVolumeJob(Render::UpdateWorldBoundingVolumeJobPtr::create())
, m_updateTreeEnabledJob(Render::UpdateTreeEnabledJobPtr::create())
, m_sendRenderCaptureJob(Render::SendRenderCaptureJobPtr::create(this))
+ , m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create())
, m_updateLevelOfDetailJob(Render::UpdateLevelOfDetailJobPtr::create())
, m_updateMeshTriangleListJob(Render::UpdateMeshTriangleListJobPtr::create())
, m_filterCompatibleTechniqueJob(Render::FilterCompatibleTechniqueJobPtr::create())
, m_bufferGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering))
+ , m_vaoGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering))
, m_textureGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering))
, m_shaderGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyShaders(); }, JobTypes::DirtyShaderGathering))
, m_syncTextureLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncTextureLoading))
@@ -263,16 +274,35 @@ void Renderer::setNodeManagers(NodeManagers *managers)
m_pickBoundingVolumeJob->setManagers(m_nodesManager);
m_updateWorldBoundingVolumeJob->setManager(m_nodesManager->renderNodesManager());
m_sendRenderCaptureJob->setManagers(m_nodesManager);
+ m_sendBufferCaptureJob->setManagers(m_nodesManager);
m_updateLevelOfDetailJob->setManagers(m_nodesManager);
m_updateMeshTriangleListJob->setManagers(m_nodesManager);
m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager());
}
+void Renderer::setServices(QServiceLocator *services)
+{
+ m_services = services;
+
+ m_nodesManager->sceneManager()->setDownloadService(m_services->downloadHelperService());
+}
+
NodeManagers *Renderer::nodeManagers() const
{
return m_nodesManager;
}
+/*!
+ \internal
+
+ Return context which can be used to share resources safely
+ with qt3d main render context.
+*/
+QOpenGLContext *Renderer::shareContext() const
+{
+ return m_shareContext ? m_shareContext : m_graphicsContext->openGLContext();
+}
+
void Renderer::setOpenGLContext(QOpenGLContext *context)
{
m_glContext = context;
@@ -314,6 +344,10 @@ void Renderer::initialize()
// Context is not owned by us, so we need to know if it gets destroyed
m_contextConnection = QObject::connect(m_glContext, &QOpenGLContext::aboutToBeDestroyed,
[this] { releaseGraphicsResources(); });
+ m_shareContext = new QOpenGLContext;
+ m_shareContext->setFormat(m_glContext->format());
+ m_shareContext->setShareContext(m_glContext);
+ m_shareContext->create();
}
// Note: we don't have a surface at this point
@@ -358,6 +392,7 @@ void Renderer::shutdown()
// to be ready. The isReadyToSubmit() function checks for a shutdown
// having been requested.
m_submitRenderViewsSemaphore.release(1);
+ m_renderThread->wait();
}
}
@@ -405,6 +440,13 @@ void Renderer::releaseGraphicsResources()
buffer->destroy(m_graphicsContext.data());
}
+ // Do the same thing with VAOs
+ const QVector<HVao> activeVaos = m_nodesManager->vaoManager()->activeHandles();
+ for (const HVao &vaoHandle : activeVaos) {
+ OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(vaoHandle);
+ vao->destroy();
+ }
+
context->doneCurrent();
} else {
qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
@@ -412,6 +454,8 @@ void Renderer::releaseGraphicsResources()
if (m_ownedContext)
delete context;
+ if (m_shareContext)
+ delete m_shareContext;
m_graphicsContext.reset(nullptr);
qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown";
@@ -500,17 +544,26 @@ void Renderer::render()
void Renderer::doRender()
{
- bool submissionSucceeded = false;
- bool hasCleanedQueueAndProceeded = false;
Renderer::ViewSubmissionResultData submissionData;
+ bool hasCleanedQueueAndProceeded = false;
bool preprocessingComplete = false;
-
- if (isReadyToSubmit()) {
-
- // Lock the mutex to protect access to m_surface and check if we are still set
- // to the running state and that we have a valid surface on which to draw
- // TO DO: Is that still needed given the surface changes
- QMutexLocker locker(&m_mutex);
+ bool beganDrawing = false;
+ const bool canSubmit = isReadyToSubmit();
+
+ // Lock the mutex to protect access to the renderQueue while we look for its state
+ QMutexLocker locker(&m_renderQueueMutex);
+ const bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
+
+ // When using synchronous rendering (QtQuick)
+ // We are not sure that the frame queue is actually complete
+ // Since a call to render may not be synched with the completions
+ // of the RenderViewJobs
+ // In such a case we return early, waiting for a next call with
+ // the frame queue complete at this point
+
+ // RenderQueue is complete (but that means it may be of size 0)
+ if (canSubmit && (queueIsComplete && !queueIsEmpty)) {
const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue();
#ifdef QT3D_JOBS_RUN_STATS
@@ -525,8 +578,7 @@ void Renderer::doRender()
submissionStatsPart2.jobId.typeAndInstance[1] = 0;
submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
#endif
-
- if (canRender() && (submissionSucceeded = renderViews.size() > 0) == true) {
+ if (canRender()) {
// Clear all dirty flags but Compute so that
// we still render every frame when a compute shader is used in a scene
BackendNodeDirtySet changesToUnset = m_changeSet;
@@ -548,7 +600,8 @@ void Renderer::doRender()
// Reset state for each draw if we don't have complete control of the context
if (!m_ownedContext)
m_graphicsContext->setCurrentStateSet(nullptr);
- if (m_graphicsContext->beginDrawing(surface)) {
+ beganDrawing = m_graphicsContext->beginDrawing(surface);
+ if (beganDrawing) {
// 1) Execute commands for buffer uploads, texture updates, shader loading first
updateGLResources();
// 2) Update VAO and copy data into commands to allow concurrent submission
@@ -559,6 +612,7 @@ void Renderer::doRender()
}
// 2) Proceed to next frame and start preparing frame n + 1
m_renderQueue->reset();
+ locker.unlock(); // Done protecting RenderQueue
m_vsyncFrameAdvanceService->proceedToNextFrame();
hasCleanedQueueAndProceeded = true;
@@ -602,61 +656,57 @@ void Renderer::doRender()
#endif
}
- // Note: submissionSucceeded is false when
- // * we cannot render because a shutdown has been scheduled
- // * the renderqueue is incomplete (only when rendering with a Scene3D)
- // Otherwise returns true even for cases like
- // * No render view
- // * No surface set
- // * OpenGLContext failed to be set current
- // This behavior is important as we need to
- // call proceedToNextFrame despite rendering errors that aren't fatal
-
// Only reset renderQueue and proceed to next frame if the submission
- // succeeded or it we are using a render thread and that is wasn't performed
+ // succeeded or if we are using a render thread and that is wasn't performed
// already
- // If submissionSucceeded isn't true this implies that something went wrong
+ // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong
// with the rendering and/or the renderqueue is incomplete from some reason
// (in the case of scene3d the render jobs may be taking too long ....)
- if (m_renderThread || submissionSucceeded) {
-
- if (!hasCleanedQueueAndProceeded) {
- // Reset the m_renderQueue so that we won't try to render
- // with a queue used by a previous frame with corrupted content
- // if the current queue was correctly submitted
- m_renderQueue->reset();
-
- // We allow the RenderTickClock service to proceed to the next frame
- // In turn this will allow the aspect manager to request a new set of jobs
- // to be performed for each aspect
- m_vsyncFrameAdvanceService->proceedToNextFrame();
- }
+ // or alternatively it could be complete but empty (RenderQueue of size 0)
+ if (!hasCleanedQueueAndProceeded &&
+ (m_renderThread || queueIsComplete || queueIsEmpty)) {
+ // RenderQueue was full but something bad happened when
+ // trying to render it and therefore proceedToNextFrame was not called
+ // Note: in this case the renderQueue mutex is still locked
+
+ // Reset the m_renderQueue so that we won't try to render
+ // with a queue used by a previous frame with corrupted content
+ // if the current queue was correctly submitted
+ m_renderQueue->reset();
+
+ // We allow the RenderTickClock service to proceed to the next frame
+ // In turn this will allow the aspect manager to request a new set of jobs
+ // to be performed for each aspect
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+ }
- // Perform the last swapBuffers calls after the proceedToNextFrame
- // as this allows us to gain a bit of time for the preparation of the
- // next frame
+ // Perform the last swapBuffers calls after the proceedToNextFrame
+ // as this allows us to gain a bit of time for the preparation of the
+ // next frame
+ // Finish up with last surface used in the list of RenderViews
+ if (beganDrawing) {
+ SurfaceLocker surfaceLock(submissionData.surface);
// Finish up with last surface used in the list of RenderViews
- if (submissionSucceeded) {
- SurfaceLocker surfaceLock(submissionData.surface);
- // Finish up with last surface used in the list of RenderViews
- m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO() && surfaceLock.isSurfaceValid());
- }
+ m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO() && surfaceLock.isSurfaceValid());
}
}
// Called by RenderViewJobs
+// When the frameQueue is complete and we are using a renderThread
+// we allow the render thread to proceed
void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder)
{
- QMutexLocker locker(&m_mutex); // Prevent out of order execution
+ QMutexLocker locker(&m_renderQueueMutex); // Prevent out of order execution
// We cannot use a lock free primitive here because:
// - QVector is not thread safe
// - Even if the insert is made correctly, the isFrameComplete call
// could be invalid since depending on the order of execution
// the counter could be complete but the renderview not yet added to the
// buffer depending on whichever order the cpu decides to process this
-
- if (m_renderQueue->queueRenderView(renderView, submitOrder)) {
+ const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder);
+ locker.unlock(); // We're done protecting the queue at this point
+ if (isQueueComplete) {
if (m_renderThread && m_running.load())
Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0);
m_submitRenderViewsSemaphore.release(1);
@@ -695,16 +745,6 @@ bool Renderer::isReadyToSubmit()
// something to render
// The case of shutdown should have been handled just before
Q_ASSERT(m_renderQueue->isFrameQueueComplete());
- } else {
- // When using synchronous rendering (QtQuick)
- // We are not sure that the frame queue is actually complete
- // Since a call to render may not be synched with the completions
- // of the RenderViewJobs
- // In such a case we return early, waiting for a next call with
- // the frame queue complete at this point
- QMutexLocker locker(&m_mutex);
- if (!m_renderQueue->isFrameQueueComplete())
- return false;
}
return true;
}
@@ -898,6 +938,23 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView
}
// Executed in a job
+void Renderer::lookForAbandonedVaos()
+{
+ const QVector<HVao> activeVaos = m_nodesManager->vaoManager()->activeHandles();
+ for (HVao handle : activeVaos) {
+ OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(handle);
+
+ // Make sure to only mark VAOs for deletion that were already created
+ // (ignore those that might be currently under construction in the render thread)
+ if (vao && vao->isAbandoned(m_nodesManager->geometryManager(), m_nodesManager->shaderManager())) {
+ m_abandonedVaosMutex.lock();
+ m_abandonedVaos.push_back(handle);
+ m_abandonedVaosMutex.unlock();
+ }
+ }
+}
+
+// Executed in a job
void Renderer::lookForDirtyBuffers()
{
const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
@@ -908,6 +965,17 @@ void Renderer::lookForDirtyBuffers()
}
}
+void Renderer::lookForDownloadableBuffers()
+{
+ m_downloadableBuffers.clear();
+ const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
+ for (HBuffer handle : activeBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ if (buffer->access() & QBuffer::Read)
+ m_downloadableBuffers.push_back(handle);
+ }
+}
+
// Executed in a job
void Renderer::lookForDirtyTextures()
{
@@ -935,6 +1003,8 @@ void Renderer::lookForDirtyShaders()
RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId);
HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram());
Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle);
+ if (Q_UNLIKELY(shader->hasPendingNotifications()))
+ shader->submitPendingNotifications();
if (shader != nullptr && !shader->isLoaded())
m_dirtyShaders.push_back(shaderHandle);
}
@@ -951,12 +1021,11 @@ void Renderer::updateGLResources()
const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers);
for (HBuffer handle: dirtyBufferHandles) {
Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
- // Perform data upload
// Forces creation if it doesn't exit
if (!m_graphicsContext->hasGLBufferForBuffer(buffer))
m_graphicsContext->glBufferForRenderBuffer(buffer);
- else if (buffer->isDirty()) // Otherwise update the glBuffer
- m_graphicsContext->updateBuffer(buffer);
+ // Update the glBuffer data
+ m_graphicsContext->updateBuffer(buffer);
buffer->unsetDirty();
}
}
@@ -964,10 +1033,11 @@ void Renderer::updateGLResources()
{
Profiling::GLTimeRecorder recorder(Profiling::ShaderUpload);
const QVector<HShader> dirtyShaderHandles = std::move(m_dirtyShaders);
+ ShaderManager *shaderManager = m_nodesManager->shaderManager();
for (HShader handle: dirtyShaderHandles) {
- Shader *shader = m_nodesManager->shaderManager()->data(handle);
+ Shader *shader = shaderManager->data(handle);
// Compile shader
- m_graphicsContext->loadShader(shader);
+ m_graphicsContext->loadShader(shader, shaderManager);
}
}
@@ -990,13 +1060,16 @@ void Renderer::updateGLResources()
// We can really release the texture at this point
m_nodesManager->textureManager()->releaseResource(textureCleanedUpId);
}
-
-
}
// Render Thread
void Renderer::updateTexture(Texture *texture)
{
+ // Check that the current texture images are still in place, if not, do not update
+ const bool isValid = texture->isValid();
+ if (!isValid)
+ return;
+
// For implementing unique, non-shared, non-cached textures.
// for now, every texture is shared by default
@@ -1065,10 +1138,14 @@ void Renderer::updateTexture(Texture *texture)
!glTextureManager->setParameters(glTexture, texture->parameters()))
qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setParameters failed, should be non-shared";
- if (dirtyFlags.testFlag(Texture::DirtyGenerators) &&
+ if (dirtyFlags.testFlag(Texture::DirtyImageGenerators) &&
!glTextureManager->setImages(glTexture, texture->textureImages()))
qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerators failed, should be non-shared";
+ if (dirtyFlags.testFlag(Texture::DirtyDataGenerator) &&
+ !glTextureManager->setGenerator(glTexture, texture->dataGenerator()))
+ qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerator failed, should be non-shared";
+
// Unset the dirty flag on the texture
texture->unsetDirty();
}
@@ -1083,6 +1160,17 @@ void Renderer::cleanupTexture(const Texture *texture)
glTextureManager->abandon(glTexture, texture);
}
+void Renderer::downloadGLBuffers()
+{
+ lookForDownloadableBuffers();
+ const QVector<HBuffer> downloadableHandles = std::move(m_downloadableBuffers);
+ for (HBuffer handle : downloadableHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ QByteArray content = m_graphicsContext->downloadBufferContent(buffer);
+ m_sendBufferCaptureJob->addRequest(QPair<Buffer*, QByteArray>(buffer, content));
+ }
+}
+
// Happens in RenderThread context when all RenderViewJobs are done
// Returns the id of the last bound FBO
@@ -1221,6 +1309,9 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
addRenderCaptureSendRequest(renderView->renderCaptureNodeId());
}
+ if (renderView->isDownloadBuffersEnable())
+ downloadGLBuffers();
+
frameElapsed = timer.elapsed() - frameElapsed;
qCDebug(Rendering) << Q_FUNC_INFO << "Submitted Renderview " << i + 1 << "/" << renderViewsCount << "in " << frameElapsed << "ms";
frameElapsed = timer.elapsed();
@@ -1311,6 +1402,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
m_pickBoundingVolumeJob->setFrameGraphRoot(frameGraphRoot());
m_pickBoundingVolumeJob->setRenderSettings(settings());
m_pickBoundingVolumeJob->setMouseEvents(pendingPickingEvents());
+ m_pickBoundingVolumeJob->setKeyEvents(pendingKeyEvents());
m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot());
// Set dependencies of resource gatherer
@@ -1331,25 +1423,36 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
renderBinJobs.push_back(m_worldTransformJob);
renderBinJobs.push_back(m_cleanupJob);
renderBinJobs.push_back(m_sendRenderCaptureJob);
+ renderBinJobs.push_back(m_sendBufferCaptureJob);
renderBinJobs.push_back(m_filterCompatibleTechniqueJob);
renderBinJobs.append(bufferJobs);
// Jobs to prepare GL Resource upload
renderBinJobs.push_back(m_syncTextureLoadingJob);
+ renderBinJobs.push_back(m_vaoGathererJob);
renderBinJobs.push_back(m_bufferGathererJob);
renderBinJobs.push_back(m_textureGathererJob);
renderBinJobs.push_back(m_shaderGathererJob);
- // Traverse the current framegraph. For each leaf node create a
- // RenderView and set its configuration then create a job to
- // populate the RenderView with a set of RenderCommands that get
- // their details from the RenderNodes that are visible to the
- // Camera selected by the framegraph configuration
- FrameGraphVisitor visitor(this, m_nodesManager->frameGraphManager());
- visitor.traverse(frameGraphRoot(), &renderBinJobs);
+ QMutexLocker lock(&m_renderQueueMutex);
+ if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case)
+ // Traverse the current framegraph. For each leaf node create a
+ // RenderView and set its configuration then create a job to
+ // populate the RenderView with a set of RenderCommands that get
+ // their details from the RenderNodes that are visible to the
+ // Camera selected by the framegraph configuration
+ FrameGraphVisitor visitor(m_nodesManager->frameGraphManager());
+ const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot());
+
+ const int fgBranchCount = fgLeaves.size();
+ for (int i = 0; i < fgBranchCount; ++i) {
+ RenderViewBuilder builder(fgLeaves.at(i), i, this);
+ renderBinJobs.append(builder.buildJobHierachy());
+ }
- // Set target number of RenderViews
- m_renderQueue->setTargetRenderViewCount(visitor.leafNodeCount());
+ // Set target number of RenderViews
+ m_renderQueue->setTargetRenderViewCount(fgBranchCount);
+ }
return renderBinJobs;
}
@@ -1364,6 +1467,11 @@ QAspectJobPtr Renderer::syncTextureLoadingJob()
return m_syncTextureLoadingJob;
}
+QAspectJobPtr Renderer::expandBoundingVolumeJob()
+{
+ return m_expandBoundingVolumeJob;
+}
+
QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const
{
return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data());
@@ -1474,16 +1582,15 @@ void Renderer::createOrUpdateVAO(RenderCommand *command,
HVao *previousVaoHandle,
OpenGLVertexArrayObject **vao)
{
+ const VAOIdentifier vaoKey(command->m_geometry, command->m_shader);
+
VAOManager *vaoManager = m_nodesManager->vaoManager();
- command->m_vao = vaoManager->lookupHandle(QPair<HGeometry, HShader>(command->m_geometry, command->m_shader));
+ command->m_vao = vaoManager->lookupHandle(vaoKey);
if (command->m_vao.isNull()) {
qCDebug(Rendering) << Q_FUNC_INFO << "Allocating new VAO";
- command->m_vao = vaoManager->getOrAcquireHandle(QPair<HGeometry, HShader>(command->m_geometry, command->m_shader));
- vaoManager->data(command->m_vao)->setGraphicsContext(m_graphicsContext.data());
- if (m_graphicsContext->supportsVAO())
- vaoManager->data(command->m_vao)->setVao(new QOpenGLVertexArrayObject());
- vaoManager->data(command->m_vao)->create();
+ command->m_vao = vaoManager->getOrAcquireHandle(vaoKey);
+ vaoManager->data(command->m_vao)->create(m_graphicsContext.data(), vaoKey);
}
if (*previousVaoHandle != command->m_vao) {
@@ -1679,11 +1786,30 @@ void Renderer::cleanGraphicsResources()
tex->destroyGLTexture();
delete tex;
}
+
+ // Delete abandoned VAOs
+ m_abandonedVaosMutex.lock();
+ const QVector<HVao> abandonedVaos = std::move(m_abandonedVaos);
+ m_abandonedVaosMutex.unlock();
+ for (HVao vaoHandle : abandonedVaos) {
+ // might have already been destroyed last frame, but added by the cleanup job before, so
+ // check if the VAO is really still existent
+ OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(vaoHandle);
+ if (vao) {
+ vao->destroy();
+ m_nodesManager->vaoManager()->release(vaoHandle);
+ }
+ }
}
QList<QMouseEvent> Renderer::pendingPickingEvents() const
{
- return m_pickEventFilter->pendingEvents();
+ return m_pickEventFilter->pendingMouseEvents();
+}
+
+QList<QKeyEvent> Renderer::pendingKeyEvents() const
+{
+ return m_pickEventFilter->pendingKeyEvents();
}
const GraphicsApiFilterData *Renderer::contextInfo() const