diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2018-03-02 10:27:07 +0100 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2018-03-16 11:21:02 +0000 |
commit | c1b495f70c3b4f0c23d44dfcd99aed5882312100 (patch) | |
tree | ced8484d36d999395f520f2d7262d832afc73c99 | |
parent | 02dff327fae71d6efdda091f8ae0a4483e72e849 (diff) |
Split GraphicsContext in two
- GraphicsContext is now a stateless wrapper around GL calls
- SubmissionContext is a GraphicsContext + caches and states used to render a
frame
Change-Id: I147b56dfd4c403911faadc0e9821fff9a892f71c
Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r-- | src/render/backend/commandexecuter.cpp | 3 | ||||
-rw-r--r-- | src/render/backend/offscreensurfacehelper_p.h | 2 | ||||
-rw-r--r-- | src/render/backend/openglvertexarrayobject.cpp | 12 | ||||
-rw-r--r-- | src/render/backend/openglvertexarrayobject_p.h | 14 | ||||
-rw-r--r-- | src/render/backend/renderer.cpp | 167 | ||||
-rw-r--r-- | src/render/backend/renderer_p.h | 8 | ||||
-rw-r--r-- | src/render/graphicshelpers/graphicscontext.cpp | 1119 | ||||
-rw-r--r-- | src/render/graphicshelpers/graphicscontext_p.h | 186 | ||||
-rw-r--r-- | src/render/graphicshelpers/graphicshelpers.pri | 6 | ||||
-rw-r--r-- | src/render/graphicshelpers/submissioncontext.cpp | 1211 | ||||
-rw-r--r-- | src/render/graphicshelpers/submissioncontext_p.h | 250 | ||||
-rw-r--r-- | src/render/jobs/filtercompatibletechniquejob.cpp | 4 | ||||
-rw-r--r-- | src/render/renderstates/renderstates.cpp | 1 | ||||
-rw-r--r-- | src/render/renderstates/renderstateset.cpp | 6 | ||||
-rw-r--r-- | src/render/renderstates/renderstateset_p.h | 6 | ||||
-rw-r--r-- | tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp | 6 | ||||
-rw-r--r-- | tests/auto/render/renderer/tst_renderer.cpp | 9 |
17 files changed, 1631 insertions, 1379 deletions
diff --git a/src/render/backend/commandexecuter.cpp b/src/render/backend/commandexecuter.cpp index cd1ec0e2b..182ba89d1 100644 --- a/src/render/backend/commandexecuter.cpp +++ b/src/render/backend/commandexecuter.cpp @@ -45,6 +45,7 @@ #include <Qt3DRender/private/nodemanagers_p.h> #include <Qt3DRender/private/geometryrenderermanager_p.h> #include <Qt3DRender/private/stringtoint_p.h> +#include <Qt3DRender/private/submissioncontext_p.h> #include <QJsonObject> #include <QJsonDocument> #include <QJsonArray> @@ -310,7 +311,7 @@ void CommandExecuter::performAsynchronousCommandExecution(const QVector<Render:: for (auto *reply : shellCommands) { if (reply->commandName() == QLatin1String("glinfo")) { QJsonObject replyObj; - const GraphicsApiFilterData *contextInfo = m_renderer->m_graphicsContext->contextInfo(); + const GraphicsApiFilterData *contextInfo = m_renderer->submissionContext()->contextInfo(); if (contextInfo != nullptr) { replyObj.insert(QLatin1String("api"), contextInfo->m_api == QGraphicsApiFilter::OpenGL diff --git a/src/render/backend/offscreensurfacehelper_p.h b/src/render/backend/offscreensurfacehelper_p.h index a2c383162..8dc86d9a4 100644 --- a/src/render/backend/offscreensurfacehelper_p.h +++ b/src/render/backend/offscreensurfacehelper_p.h @@ -62,7 +62,7 @@ namespace Render { class AbstractRenderer; -class OffscreenSurfaceHelper : public QObject +class Q_AUTOTEST_EXPORT OffscreenSurfaceHelper : public QObject { Q_OBJECT public: diff --git a/src/render/backend/openglvertexarrayobject.cpp b/src/render/backend/openglvertexarrayobject.cpp index c0fdd8e65..0c4fd8c9d 100644 --- a/src/render/backend/openglvertexarrayobject.cpp +++ b/src/render/backend/openglvertexarrayobject.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "openglvertexarrayobject_p.h" -#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/submissioncontext_p.h> #include <Qt3DRender/private/renderer_p.h> #include <Qt3DRender/private/nodemanagers_p.h> #include <Qt3DRender/private/managers_p.h> @@ -68,7 +68,7 @@ void OpenGLVertexArrayObject::bind() m_ctx->m_currentVAO = this; // We need to specify array and vertex attributes - for (const GraphicsContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) + for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) m_ctx->enableAttribute(attr); if (!m_indexAttribute.isNull()) m_ctx->bindGLBuffer(m_ctx->m_renderer->nodeManagers()->glBufferManager()->data(m_indexAttribute), @@ -85,7 +85,7 @@ void OpenGLVertexArrayObject::release() m_vao->release(); } else { if (m_ctx->m_currentVAO == this) { - for (const GraphicsContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) + for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) m_ctx->disableAttribute(attr); m_ctx->m_currentVAO = nullptr; } @@ -93,7 +93,7 @@ void OpenGLVertexArrayObject::release() } // called from Render thread -void OpenGLVertexArrayObject::create(GraphicsContext *ctx, const VAOIdentifier &key) +void OpenGLVertexArrayObject::create(SubmissionContext *ctx, const VAOIdentifier &key) { QMutexLocker lock(&m_mutex); @@ -123,7 +123,7 @@ void OpenGLVertexArrayObject::cleanup() m_ctx = nullptr; m_specified = false; m_supportsVao = false; - m_indexAttribute = GraphicsContext::VAOIndexAttribute(); + m_indexAttribute = SubmissionContext::VAOIndexAttribute(); m_vertexAttributes.clear(); } @@ -141,7 +141,7 @@ bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, ShaderManage return !geometryExists || !shaderExists; } -void OpenGLVertexArrayObject::saveVertexAttribute(const GraphicsContext::VAOVertexAttribute &attr) +void OpenGLVertexArrayObject::saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr) { // Remove any vertexAttribute already at location for (auto i = m_vertexAttributes.size() - 1; i >= 0; --i) { diff --git a/src/render/backend/openglvertexarrayobject_p.h b/src/render/backend/openglvertexarrayobject_p.h index a564175f6..eee837221 100644 --- a/src/render/backend/openglvertexarrayobject_p.h +++ b/src/render/backend/openglvertexarrayobject_p.h @@ -52,7 +52,7 @@ // #include <QtGui/qopenglvertexarrayobject.h> -#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/submissioncontext_p.h> QT_BEGIN_NAMESPACE @@ -72,7 +72,7 @@ public: void bind(); void release(); - void create(GraphicsContext *ctx, const VAOIdentifier &key); + void create(SubmissionContext *ctx, const VAOIdentifier &key); void destroy(); void cleanup(); @@ -87,19 +87,19 @@ public: private: QMutex m_mutex; - GraphicsContext *m_ctx; + SubmissionContext *m_ctx; QScopedPointer<QOpenGLVertexArrayObject> m_vao; bool m_specified; bool m_supportsVao; VAOIdentifier m_owners; - friend class GraphicsContext; + friend class SubmissionContext; - void saveVertexAttribute(const GraphicsContext::VAOVertexAttribute &attr); + void saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr); inline void saveIndexAttribute(HGLBuffer glBufferHandle) { m_indexAttribute = glBufferHandle; } - QVector<GraphicsContext::VAOVertexAttribute> m_vertexAttributes; - GraphicsContext::VAOIndexAttribute m_indexAttribute; + QVector<SubmissionContext::VAOVertexAttribute> m_vertexAttributes; + SubmissionContext::VAOIndexAttribute m_indexAttribute; }; } // namespace Render diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index 68b6dd1fd..cdec9af01 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -156,7 +156,7 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_nodesManager(nullptr) , m_renderSceneRoot(nullptr) , m_defaultRenderStateSet(nullptr) - , m_graphicsContext(nullptr) + , m_submissionContext(nullptr) , m_renderQueue(new RenderQueue()) , m_renderThread(type == QRenderAspect::Threaded ? new RenderThread(this) : nullptr) , m_commandThread(new CommandThread(this)) @@ -167,6 +167,7 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_lastFrameCorrect(0) , m_glContext(nullptr) , m_shareContext(nullptr) + , m_shaderCache(new ShaderCache()) , m_pickBoundingVolumeJob(PickBoundingVolumeJobPtr::create()) , m_rayCastingJob(RayCastingJobPtr::create()) , m_time(0) @@ -242,6 +243,7 @@ Renderer::~Renderer() delete m_renderQueue; delete m_defaultRenderStateSet; + delete m_shaderCache; if (!m_ownedContext) QObject::disconnect(m_contextConnection); @@ -314,8 +316,8 @@ QOpenGLContext *Renderer::shareContext() const { QMutexLocker lock(&m_shareContextMutex); return m_shareContext ? m_shareContext - : (m_graphicsContext->openGLContext() - ? m_graphicsContext->openGLContext()->shareContext() + : (m_submissionContext->openGLContext() + ? m_submissionContext->openGLContext()->shareContext() : nullptr); } @@ -334,8 +336,8 @@ void Renderer::setOpenGLContext(QOpenGLContext *context) // method termintates void Renderer::initialize() { - m_graphicsContext.reset(new GraphicsContext); - m_graphicsContext->setRenderer(this); + m_submissionContext.reset(new SubmissionContext); + m_submissionContext->setRenderer(this); QOpenGLContext* ctx = m_glContext; @@ -376,22 +378,22 @@ void Renderer::initialize() m_shareContext->create(); } + // Set shader cache on submission context and command thread + m_submissionContext->setShaderCache(m_shaderCache); + // Note: we don't have a surface at this point // The context will be made current later on (at render time) - m_graphicsContext->setOpenGLContext(ctx); - - // Initialize command thread - m_commandThread->initialize(ctx); + m_submissionContext->setOpenGLContext(ctx); + + // Store the format used by the context and queue up creating an + // offscreen surface in the main thread so that it is available + // for use when we want to shutdown the renderer. We need to create + // the offscreen surface on the main thread because on some platforms + // (MS Windows), an offscreen surface is just a hidden QWindow. + m_format = ctx->format(); + QMetaObject::invokeMethod(m_offscreenHelper, "createOffscreenSurface"); } - // Store the format used by the context and queue up creating an - // offscreen surface in the main thread so that it is available - // for use when we want to shutdown the renderer. We need to create - // the offscreen surface on the main thread because on some platforms - // (MS Windows), an offscreen surface is just a hidden QWindow. - m_format = ctx->format(); - QMetaObject::invokeMethod(m_offscreenHelper, "createOffscreenSurface"); - // Awake setScenegraphRoot in case it was waiting m_waitForInitializationToBeCompleted.release(1); // Allow the aspect manager to proceed @@ -445,7 +447,7 @@ void Renderer::releaseGraphicsResources() // We may get called twice when running inside of a Scene3D. Once when Qt Quick // wants to shutdown, and again when the render aspect gets unregistered. So // check that we haven't already cleaned up before going any further. - if (!m_graphicsContext) + if (!m_submissionContext) return; // Try to temporarily make the context current so we can free up any resources @@ -456,7 +458,7 @@ void Renderer::releaseGraphicsResources() return; } - QOpenGLContext *context = m_graphicsContext->openGLContext(); + QOpenGLContext *context = m_submissionContext->openGLContext(); Q_ASSERT(context); if (context->makeCurrent(offscreenSurface)) { @@ -469,7 +471,7 @@ void Renderer::releaseGraphicsResources() const QVector<HGLBuffer> activeBuffers = m_nodesManager->glBufferManager()->activeHandles(); for (const HGLBuffer &bufferHandle : activeBuffers) { GLBuffer *buffer = m_nodesManager->glBufferManager()->data(bufferHandle); - buffer->destroy(m_graphicsContext.data()); + buffer->destroy(m_submissionContext.data()); } // Do the same thing with VAOs @@ -489,7 +491,7 @@ void Renderer::releaseGraphicsResources() if (m_shareContext) delete m_shareContext; - m_graphicsContext.reset(nullptr); + m_submissionContext.reset(nullptr); qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown"; } @@ -645,8 +647,8 @@ void Renderer::doRender(bool scene3dBlocking) if (surfaceIsValid) { // Reset state for each draw if we don't have complete control of the context if (!m_ownedContext) - m_graphicsContext->setCurrentStateSet(nullptr); - beganDrawing = m_graphicsContext->beginDrawing(surface); + m_submissionContext->setCurrentStateSet(nullptr); + beganDrawing = m_submissionContext->beginDrawing(surface); if (beganDrawing) { // 1) Execute commands for buffer uploads, texture updates, shader loading first updateGLResources(); @@ -734,7 +736,7 @@ void Renderer::doRender(bool scene3dBlocking) if (beganDrawing) { 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_submissionContext->endDrawing(submissionData.lastBoundFBOId == m_submissionContext->defaultFBO() && surfaceLock.isSurfaceValid()); } } @@ -868,7 +870,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView if (!command->m_attributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) { Profiling::GLTimeRecorder recorder(Profiling::VAOUpload); // Activate shader - m_graphicsContext->activateShader(shader->dna()); + m_submissionContext->activateShader(shader->dna()); // Bind VAO vao->bind(); // Update or set Attributes and Buffers for the given rGeometry and Command @@ -1117,10 +1119,10 @@ void Renderer::updateGLResources() Buffer *buffer = m_nodesManager->bufferManager()->data(handle); // Forces creation if it doesn't exit // Also note the binding point doesn't really matter here, we just upload data - if (!m_graphicsContext->hasGLBufferForBuffer(buffer)) - m_graphicsContext->glBufferForRenderBuffer(buffer, GLBuffer::ArrayBuffer); + if (!m_submissionContext->hasGLBufferForBuffer(buffer)) + m_submissionContext->glBufferForRenderBuffer(buffer, GLBuffer::ArrayBuffer); // Update the glBuffer data - m_graphicsContext->updateBuffer(buffer); + m_submissionContext->updateBuffer(buffer); buffer->unsetDirty(); } } @@ -1132,7 +1134,7 @@ void Renderer::updateGLResources() for (const HShader &handle: dirtyShaderHandles) { Shader *shader = shaderManager->data(handle); // Compile shader - m_graphicsContext->loadShader(shader, shaderManager); + m_submissionContext->loadShader(shader, shaderManager); } } @@ -1263,7 +1265,7 @@ void Renderer::downloadGLBuffers() const QVector<HBuffer> downloadableHandles = std::move(m_downloadableBuffers); for (const HBuffer &handle : downloadableHandles) { Buffer *buffer = m_nodesManager->bufferManager()->data(handle); - QByteArray content = m_graphicsContext->downloadBufferContent(buffer); + QByteArray content = m_submissionContext->downloadBufferContent(buffer); m_sendBufferCaptureJob->addRequest(QPair<Buffer*, QByteArray>(buffer, content)); } } @@ -1283,7 +1285,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren qCDebug(Memory) << Q_FUNC_INFO << "rendering frame "; // We might not want to render on the default FBO - uint lastBoundFBOId = m_graphicsContext->boundFrameBufferObject(); + uint lastBoundFBOId = m_submissionContext->boundFrameBufferObject(); QSurface *surface = nullptr; QSurface *previousSurface = nullptr; for (const Render::RenderView *rv: renderViews) { @@ -1319,27 +1321,27 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren const bool surfaceHasChanged = surface != previousSurface; if (surfaceHasChanged && previousSurface) { - const bool swapBuffers = (lastBoundFBOId == m_graphicsContext->defaultFBO()) && PlatformSurfaceFilter::isSurfaceValid(previousSurface); + const bool swapBuffers = (lastBoundFBOId == m_submissionContext->defaultFBO()) && PlatformSurfaceFilter::isSurfaceValid(previousSurface); // We only call swap buffer if we are sure the previous surface is still valid - m_graphicsContext->endDrawing(swapBuffers); + m_submissionContext->endDrawing(swapBuffers); } if (surfaceHasChanged) { // If we can't make the context current on the surface, skip to the // next RenderView. We won't get the full frame but we may get something - if (!m_graphicsContext->beginDrawing(surface)) { + if (!m_submissionContext->beginDrawing(surface)) { qWarning() << "Failed to make OpenGL context current on surface"; m_lastFrameCorrect.store(0); continue; } previousSurface = surface; - lastBoundFBOId = m_graphicsContext->boundFrameBufferObject(); + lastBoundFBOId = m_submissionContext->boundFrameBufferObject(); } // Apply Memory Barrier if needed if (renderView->memoryBarrier() != QMemoryBarrier::None) - m_graphicsContext->memoryBarrier(renderView->memoryBarrier()); + m_submissionContext->memoryBarrier(renderView->memoryBarrier()); // Note: the RenderStateSet is allocated once per RV if needed // and it contains a list of StateVariant value types @@ -1349,16 +1351,16 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren Profiling::GLTimeRecorder recorder(Profiling::StateUpdate); // Set the RV state if not null, if (renderViewStateSet != nullptr) - m_graphicsContext->setCurrentStateSet(renderViewStateSet); + m_submissionContext->setCurrentStateSet(renderViewStateSet); else - m_graphicsContext->setCurrentStateSet(m_defaultRenderStateSet); + m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet); } // Set RenderTarget ... // Activate RenderTarget { Profiling::GLTimeRecorder recorder(Profiling::RenderTargetUpdate); - m_graphicsContext->activateRenderTarget(renderView->renderTargetId(), + m_submissionContext->activateRenderTarget(renderView->renderTargetId(), renderView->attachmentPack(), lastBoundFBOId); } @@ -1369,25 +1371,25 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren auto clearBufferTypes = renderView->clearTypes(); if (clearBufferTypes & QClearBuffers::ColorBuffer) { const QVector4D vCol = renderView->globalClearColorBufferInfo().clearColor; - m_graphicsContext->clearColor(QColor::fromRgbF(vCol.x(), vCol.y(), vCol.z(), vCol.w())); + m_submissionContext->clearColor(QColor::fromRgbF(vCol.x(), vCol.y(), vCol.z(), vCol.w())); } if (clearBufferTypes & QClearBuffers::DepthBuffer) - m_graphicsContext->clearDepthValue(renderView->clearDepthValue()); + m_submissionContext->clearDepthValue(renderView->clearDepthValue()); if (clearBufferTypes & QClearBuffers::StencilBuffer) - m_graphicsContext->clearStencilValue(renderView->clearStencilValue()); + m_submissionContext->clearStencilValue(renderView->clearStencilValue()); // Clear BackBuffer - m_graphicsContext->clearBackBuffer(clearBufferTypes); + m_submissionContext->clearBackBuffer(clearBufferTypes); // if there are ClearColors set for different draw buffers, // clear each of these draw buffers individually now const QVector<ClearBufferInfo> clearDrawBuffers = renderView->specificClearColorBufferInfo(); for (const ClearBufferInfo &clearBuffer : clearDrawBuffers) - m_graphicsContext->clearBufferf(clearBuffer.drawBufferIndex, clearBuffer.clearColor); + m_submissionContext->clearBufferf(clearBuffer.drawBufferIndex, clearBuffer.clearColor); } // Set the Viewport - m_graphicsContext->setViewport(renderView->viewport(), renderView->surfaceSize() * renderView->devicePixelRatio()); + m_submissionContext->setViewport(renderView->viewport(), renderView->surfaceSize() * renderView->devicePixelRatio()); // Execute the render commands if (!executeCommandsSubmission(renderView)) @@ -1398,15 +1400,15 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren // renderViewStateSet or m_defaultRenderStateSet) if (!renderView->renderCaptureNodeId().isNull()) { const QRenderCaptureRequest request = renderView->renderCaptureRequest(); - const QSize size = m_graphicsContext->renderTargetSize(renderView->surfaceSize() * renderView->devicePixelRatio()); + const QSize size = m_submissionContext->renderTargetSize(renderView->surfaceSize() * renderView->devicePixelRatio()); QRect rect(QPoint(0, 0), size); if (!request.rect.isEmpty()) rect = rect.intersected(request.rect); QImage image; if (!rect.isEmpty()) { // Bind fbo as read framebuffer - m_graphicsContext->bindFramebuffer(m_graphicsContext->activeFBO(), GraphicsHelperInterface::FBORead); - image = m_graphicsContext->readFramebuffer(rect); + m_submissionContext->bindFramebuffer(m_submissionContext->activeFBO(), GraphicsHelperInterface::FBORead); + image = m_submissionContext->readFramebuffer(rect); } else { qWarning() << "Requested capture rectangle is outside framebuffer"; } @@ -1429,9 +1431,9 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren const QRenderTargetOutput::AttachmentPoint inputAttachmentPoint = blitFramebufferInfo.sourceAttachmentPoint; const QRenderTargetOutput::AttachmentPoint outputAttachmentPoint = blitFramebufferInfo.destinationAttachmentPoint; const QBlitFramebuffer::InterpolationMethod interpolationMethod = blitFramebufferInfo.interpolationMethod; - m_graphicsContext->blitFramebuffer(inputTargetId, outputTargetId, inputRect, outputRect, lastBoundFBOId, - inputAttachmentPoint, outputAttachmentPoint, - interpolationMethod); + m_submissionContext->blitFramebuffer(inputTargetId, outputTargetId, inputRect, outputRect, lastBoundFBOId, + inputAttachmentPoint, outputAttachmentPoint, + interpolationMethod); } @@ -1443,16 +1445,16 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren // Bind lastBoundFBOId back. Needed also in threaded mode. // lastBoundFBOId != m_graphicsContext->activeFBO() when the last FrameGraph leaf node/renderView // contains RenderTargetSelector/RenderTarget - if (lastBoundFBOId != m_graphicsContext->activeFBO()) - m_graphicsContext->bindFramebuffer(lastBoundFBOId, GraphicsHelperInterface::FBOReadAndDraw); + if (lastBoundFBOId != m_submissionContext->activeFBO()) + m_submissionContext->bindFramebuffer(lastBoundFBOId, GraphicsHelperInterface::FBOReadAndDraw); // Reset state and call doneCurrent if the surface // is valid and was actually activated - if (surface && m_graphicsContext->hasValidGLHelper()) { + if (surface && m_submissionContext->hasValidGLHelper()) { // Reset state to the default state if the last stateset is not the // defaultRenderStateSet - if (m_graphicsContext->currentStateSet() != m_defaultRenderStateSet) - m_graphicsContext->setCurrentStateSet(m_defaultRenderStateSet); + if (m_submissionContext->currentStateSet() != m_defaultRenderStateSet) + m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet); } queueElapsed = timer.elapsed() - queueElapsed; @@ -1618,7 +1620,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() notCleared |= AbstractRenderer::LayersDirty; } - if (isRunning() && m_graphicsContext->isInitialized()) { + if (isRunning() && m_submissionContext->isInitialized()) { if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty ) renderBinJobs.push_back(m_filterCompatibleTechniqueJob); if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty) @@ -1688,23 +1690,23 @@ void Renderer::performDraw(RenderCommand *command) } // Get GLBuffer from Buffer; - GLBuffer *indirectDrawGLBuffer = m_graphicsContext->glBufferForRenderBuffer(indirectDrawBuffer, GLBuffer::DrawIndirectBuffer); + GLBuffer *indirectDrawGLBuffer = m_submissionContext->glBufferForRenderBuffer(indirectDrawBuffer, GLBuffer::DrawIndirectBuffer); if (Q_UNLIKELY(indirectDrawGLBuffer == nullptr)) { qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve GLBuffer"; return; } // Bind GLBuffer - const bool successfullyBound = indirectDrawGLBuffer->bind(m_graphicsContext.data(), GLBuffer::DrawIndirectBuffer); + const bool successfullyBound = indirectDrawGLBuffer->bind(m_submissionContext.data(), GLBuffer::DrawIndirectBuffer); if (Q_LIKELY(successfullyBound)) { // TO DO: Handle multi draw variants if attribute count > 1 if (command->m_drawIndexed) { - m_graphicsContext->drawElementsIndirect(command->m_primitiveType, + m_submissionContext->drawElementsIndirect(command->m_primitiveType, command->m_indexAttributeDataType, reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset))); } else { - m_graphicsContext->drawArraysIndirect(command->m_primitiveType, + m_submissionContext->drawArraysIndirect(command->m_primitiveType, reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset))); } } else { @@ -1712,17 +1714,18 @@ void Renderer::performDraw(RenderCommand *command) } } else { // Direct Draw Calls + // TO DO: Add glMulti Draw variants if (command->m_primitiveType == QGeometryRenderer::Patches) - m_graphicsContext->setVerticesPerPatch(command->m_verticesPerPatch); + m_submissionContext->setVerticesPerPatch(command->m_verticesPerPatch); if (command->m_primitiveRestartEnabled) - m_graphicsContext->enablePrimitiveRestart(command->m_restartIndexValue); + m_submissionContext->enablePrimitiveRestart(command->m_restartIndexValue); // TO DO: Add glMulti Draw variants if (command->m_drawIndexed) { Profiling::GLTimeRecorder recorder(Profiling::DrawElement); - m_graphicsContext->drawElementsInstancedBaseVertexBaseInstance(command->m_primitiveType, + m_submissionContext->drawElementsInstancedBaseVertexBaseInstance(command->m_primitiveType, command->m_primitiveCount, command->m_indexAttributeDataType, reinterpret_cast<void*>(quintptr(command->m_indexAttributeByteOffset)), @@ -1731,7 +1734,7 @@ void Renderer::performDraw(RenderCommand *command) command->m_firstVertex); } else { Profiling::GLTimeRecorder recorder(Profiling::DrawArray); - m_graphicsContext->drawArraysInstancedBaseInstance(command->m_primitiveType, + m_submissionContext->drawArraysInstancedBaseInstance(command->m_primitiveType, command->m_firstInstance, command->m_primitiveCount, command->m_instanceCount, @@ -1746,22 +1749,22 @@ void Renderer::performDraw(RenderCommand *command) #endif if (command->m_primitiveRestartEnabled) - m_graphicsContext->disablePrimitiveRestart(); + m_submissionContext->disablePrimitiveRestart(); } void Renderer::performCompute(const RenderView *, RenderCommand *command) { { Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); - m_graphicsContext->activateShader(command->m_shaderDna); + m_submissionContext->activateShader(command->m_shaderDna); } { Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); - m_graphicsContext->setParameters(command->m_parameterPack); + m_submissionContext->setParameters(command->m_parameterPack); } { Profiling::GLTimeRecorder recorder(Profiling::DispatchCompute); - m_graphicsContext->dispatchCompute(command->m_workGroups[0], + m_submissionContext->dispatchCompute(command->m_workGroups[0], command->m_workGroups[1], command->m_workGroups[2]); } @@ -1787,7 +1790,7 @@ void Renderer::createOrUpdateVAO(RenderCommand *command, if (command->m_vao.isNull()) { qCDebug(Rendering) << Q_FUNC_INFO << "Allocating new VAO"; command->m_vao = vaoManager->getOrAcquireHandle(vaoKey); - vaoManager->data(command->m_vao)->create(m_graphicsContext.data(), vaoKey); + vaoManager->data(command->m_vao)->create(m_submissionContext.data(), vaoKey); } if (*previousVaoHandle != command->m_vao) { @@ -1810,7 +1813,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) // graphics API (OpenGL) // Save the RenderView base stateset - RenderStateSet *globalState = m_graphicsContext->currentStateSet(); + RenderStateSet *globalState = m_submissionContext->currentStateSet(); OpenGLVertexArrayObject *vao = nullptr; for (RenderCommand *command : qAsConst(commands)) { @@ -1835,7 +1838,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) { Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); //// We activate the shader here - if (!m_graphicsContext->activateShader(command->m_shaderDna)) { + if (!m_submissionContext->activateShader(command->m_shaderDna)) { allCommandsIssued = false; continue; } @@ -1850,7 +1853,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) { Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); //// Update program uniforms - if (!m_graphicsContext->setParameters(command->m_parameterPack)) { + if (!m_submissionContext->setParameters(command->m_parameterPack)) { allCommandsIssued = false; // If we have failed to set uniform (e.g unable to bind a texture) // we won't perform the draw call which could show invalid content @@ -1870,9 +1873,9 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) // Or restore the globalState if no stateSet for the RenderCommand if (localState != nullptr) { command->m_stateSet->merge(globalState); - m_graphicsContext->setCurrentStateSet(command->m_stateSet); + m_submissionContext->setCurrentStateSet(command->m_stateSet); } else { - m_graphicsContext->setCurrentStateSet(globalState); + m_submissionContext->setCurrentStateSet(globalState); } } // All Uniforms for a pass are stored in the QUniformPack of the command @@ -1890,7 +1893,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) vao->release(); // Reset to the state we were in before executing the render commands - m_graphicsContext->setCurrentStateSet(globalState); + m_submissionContext->setCurrentStateSet(globalState); return allCommandsIssued; } @@ -1921,7 +1924,7 @@ bool Renderer::updateVAOWithAttributes(Geometry *geometry, bool attributeWasDirty = false; if (attribute->attributeType() == QAttribute::IndexAttribute) { if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate) - m_graphicsContext->specifyIndices(buffer); + m_submissionContext->specifyIndices(buffer); // Vertex Attribute } else if (command->m_attributes.contains(attribute->nameId())) { if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate) { @@ -1936,7 +1939,7 @@ bool Renderer::updateVAOWithAttributes(Geometry *geometry, } if (!attributeDescription || attributeDescription->m_location < 0) return false; - m_graphicsContext->specifyAttribute(attribute, buffer, attributeDescription); + m_submissionContext->specifyAttribute(attribute, buffer, attributeDescription); } } @@ -1980,7 +1983,7 @@ void Renderer::cleanGraphicsResources() // Clean buffers const QVector<Qt3DCore::QNodeId> buffersToRelease = m_nodesManager->bufferManager()->takeBuffersToRelease(); for (Qt3DCore::QNodeId bufferId : buffersToRelease) - m_graphicsContext->releaseBuffer(bufferId); + m_submissionContext->releaseBuffer(bufferId); // Delete abandoned textures const QVector<GLTexture*> abandonedTextures = m_nodesManager->glTextureManager()->takeAbandonedTextures(); @@ -2016,12 +2019,12 @@ QList<QKeyEvent> Renderer::pendingKeyEvents() const const GraphicsApiFilterData *Renderer::contextInfo() const { - return m_graphicsContext->contextInfo(); + return m_submissionContext->contextInfo(); } -GraphicsContext *Renderer::graphicsContext() const +SubmissionContext *Renderer::submissionContext() const { - return m_graphicsContext.data(); + return m_submissionContext.data(); } void Renderer::addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId) diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h index d79bf6136..d174fa428 100644 --- a/src/render/backend/renderer_p.h +++ b/src/render/backend/renderer_p.h @@ -125,7 +125,7 @@ class CommandExecuter; namespace Render { class CameraLens; -class GraphicsContext; +class SubmissionContext; class FrameGraphNode; class Material; class Technique; @@ -142,6 +142,7 @@ class RenderStateSet; class VSyncFrameAdvanceService; class PickEventFilter; class NodeManagers; +class ShaderCache; class UpdateLevelOfDetailJob; typedef QSharedPointer<UpdateLevelOfDetailJob> UpdateLevelOfDetailJobPtr; @@ -249,7 +250,7 @@ public: void setOpenGLContext(QOpenGLContext *context); const GraphicsApiFilterData *contextInfo() const; - GraphicsContext *graphicsContext() const; + SubmissionContext *submissionContext() const; inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; } @@ -302,7 +303,7 @@ private: RenderStateSet *m_defaultRenderStateSet; ShaderParameterPack m_defaultUniformPack; - QScopedPointer<GraphicsContext> m_graphicsContext; + QScopedPointer<SubmissionContext> m_submissionContext; QSurfaceFormat m_format; RenderQueue *m_renderQueue; @@ -331,6 +332,7 @@ private: QOpenGLContext *m_glContext; QOpenGLContext *m_shareContext; mutable QMutex m_shareContextMutex; + ShaderCache *m_shaderCache; PickBoundingVolumeJobPtr m_pickBoundingVolumeJob; RayCastingJobPtr m_rayCastingJob; diff --git a/src/render/graphicshelpers/graphicscontext.cpp b/src/render/graphicshelpers/graphicscontext.cpp index 93f924052..ddd7281d1 100644 --- a/src/render/graphicshelpers/graphicscontext.cpp +++ b/src/render/graphicshelpers/graphicscontext.cpp @@ -111,88 +111,45 @@ QOpenGLShader::ShaderType shaderType(Qt3DRender::QShaderProgram::ShaderType type namespace Qt3DRender { namespace Render { -static QHash<unsigned int, GraphicsContext*> static_contexts; - -static void logOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage) -{ - qDebug() << "OpenGL debug message:" << debugMessage; -} - namespace { -GLBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type) +void logOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage) { - switch (type) { - case QAttribute::VertexAttribute: - return GLBuffer::ArrayBuffer; - case QAttribute::IndexAttribute: - return GLBuffer::IndexBuffer; - case QAttribute::DrawIndirectAttribute: - return GLBuffer::DrawIndirectBuffer; - default: - Q_UNREACHABLE(); - } + qDebug() << "OpenGL debug message:" << debugMessage; } } // anonymous -unsigned int nextFreeContextId() -{ - for (unsigned int i=0; i < 0xffff; ++i) { - if (!static_contexts.contains(i)) - return i; - } - - qFatal("Couldn't find free context ID"); - return 0; -} - GraphicsContext::GraphicsContext() : m_initialized(false) - , m_id(nextFreeContextId()) + , m_supportsVAO(false) + , m_maxTextureUnits(0) + , m_defaultFBO(0) , m_gl(nullptr) - , m_surface(nullptr) , m_glHelper(nullptr) - , m_ownCurrent(true) - , m_activeShader(nullptr) - , m_activeShaderDNA(0) - , m_renderTargetFormat(QAbstractTexture::NoFormat) - , m_currClearStencilValue(0) - , m_currClearDepthValue(1.f) - , m_currClearColorValue(0,0,0,0) - , m_material(nullptr) - , m_activeFBO(0) - , m_defaultFBO(0) - , m_boundArrayBuffer(nullptr) - , m_stateSet(nullptr) - , m_renderer(nullptr) - , m_uboTempArray(QByteArray(1024, 0)) - , m_supportsVAO(true) + , m_shaderCache(nullptr) , m_debugLogger(nullptr) - , m_currentVAO(nullptr) { - static_contexts[m_id] = this; } GraphicsContext::~GraphicsContext() { - releaseOpenGL(); +} - Q_ASSERT(static_contexts[m_id] == this); - static_contexts.remove(m_id); +void GraphicsContext::setOpenGLContext(QOpenGLContext* ctx) +{ + Q_ASSERT(ctx); + m_gl = ctx; } void GraphicsContext::initialize() { m_initialized = true; - Q_ASSERT(m_gl); + Q_ASSERT(m_gl && m_shaderCache); - GLint numTexUnits; - m_gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTexUnits); - qCDebug(Backend) << "context supports" << numTexUnits << "texture units"; - - m_activeTextures.resize(numTexUnits); + m_gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &m_maxTextureUnits); + qCDebug(Backend) << "context supports" << m_maxTextureUnits << "texture units"; if (m_gl->format().majorVersion() >= 3) { m_supportsVAO = true; @@ -207,96 +164,6 @@ void GraphicsContext::initialize() qCDebug(Backend) << "VAO support = " << m_supportsVAO; } -void GraphicsContext::resolveRenderTargetFormat() -{ - const QSurfaceFormat format = m_gl->format(); - const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize(); - const uint r = format.redBufferSize(); - const uint g = format.greenBufferSize(); - const uint b = format.blueBufferSize(); - -#define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18)) - - const uint bits = RGBA_BITS(r,g,b,a); - switch (bits) { - case RGBA_BITS(8,8,8,8): - m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm; - break; - case RGBA_BITS(8,8,8,0): - m_renderTargetFormat = QAbstractTexture::RGB8_UNorm; - break; - case RGBA_BITS(5,6,5,0): - m_renderTargetFormat = QAbstractTexture::R5G6B5; - break; - } -#undef RGBA_BITS -} - -bool GraphicsContext::beginDrawing(QSurface *surface) -{ - Q_ASSERT(surface); - Q_ASSERT(m_gl); - - m_surface = surface; - - // TO DO: Find a way to make to pause work if the window is not exposed - // if (m_surface && m_surface->surfaceClass() == QSurface::Window) { - // qDebug() << Q_FUNC_INFO << 1; - // if (!static_cast<QWindow *>(m_surface)->isExposed()) - // return false; - // qDebug() << Q_FUNC_INFO << 2; - // } - - // Makes the surface current on the OpenGLContext - // and sets the right glHelper - m_ownCurrent = !(m_gl->surface() == m_surface); - if (m_ownCurrent && !makeCurrent(m_surface)) - return false; - - // TODO: cache surface format somewhere rather than doing this every time render surface changes - resolveRenderTargetFormat(); - - // Sets or Create the correct m_glHelper - // for the current surface - activateGLHelper(); - -#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) - GLint err = m_gl->functions()->glGetError(); - if (err != 0) { - qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err; - } -#endif - - if (!m_initialized) { - initialize(); - } - - // need to reset these values every frame, may get overwritten elsewhere - m_gl->functions()->glClearColor(m_currClearColorValue.redF(), m_currClearColorValue.greenF(), m_currClearColorValue.blueF(), m_currClearColorValue.alphaF()); - m_gl->functions()->glClearDepthf(m_currClearDepthValue); - m_gl->functions()->glClearStencil(m_currClearStencilValue); - - - if (m_activeShader) { - m_activeShader = nullptr; - m_activeShaderDNA = 0; - } - - // reset active textures - for (int u = 0; u < m_activeTextures.size(); ++u) - m_activeTextures[u].texture = nullptr; - - m_boundArrayBuffer = nullptr; - - static int callCount = 0; - ++callCount; - const int shaderPurgePeriod = 600; - if (callCount % shaderPurgePeriod == 0) - m_shaderCache.purge(); - - return true; -} - void GraphicsContext::clearBackBuffer(QClearBuffers::BufferTypeFlags buffers) { if (buffers != QClearBuffers::None) { @@ -313,120 +180,6 @@ void GraphicsContext::clearBackBuffer(QClearBuffers::BufferTypeFlags buffers) } } -void GraphicsContext::endDrawing(bool swapBuffers) -{ - if (swapBuffers) - m_gl->swapBuffers(m_surface); - if (m_ownCurrent) - m_gl->doneCurrent(); - decayTextureScores(); -} - -QSize GraphicsContext::renderTargetSize(const QSize &surfaceSize) const -{ - QSize renderTargetSize; - if (m_activeFBO != m_defaultFBO) { - // For external FBOs we may not have a m_renderTargets entry. - if (m_renderTargetsSize.contains(m_activeFBO)) { - renderTargetSize = m_renderTargetsSize[m_activeFBO]; - } else if (surfaceSize.isValid()) { - renderTargetSize = surfaceSize; - } else { - // External FBO (when used with QtQuick2 Scene3D) - - // Query FBO color attachment 0 size - GLint attachmentObjectType = GL_NONE; - GLint attachment0Name = 0; - m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, - &attachmentObjectType); - m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, - &attachment0Name); - - if (attachmentObjectType == GL_RENDERBUFFER && m_glHelper->supportsFeature(GraphicsHelperInterface::RenderBufferDimensionRetrieval)) - renderTargetSize = m_glHelper->getRenderBufferDimensions(attachment0Name); - else if (attachmentObjectType == GL_TEXTURE && m_glHelper->supportsFeature(GraphicsHelperInterface::TextureDimensionRetrieval)) - // Assumes texture level 0 and GL_TEXTURE_2D target - renderTargetSize = m_glHelper->getTextureDimensions(attachment0Name, GL_TEXTURE_2D); - else - return renderTargetSize; - } - } else { - renderTargetSize = m_surface->size(); - if (m_surface->surfaceClass() == QSurface::Window) { - int dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio(); - renderTargetSize *= dpr; - } - } - return renderTargetSize; -} - -void GraphicsContext::setViewport(const QRectF &viewport, const QSize &surfaceSize) -{ - // save for later use; this has nothing to do with the viewport but it is - // here that we get to know the surfaceSize from the RenderView. - m_surfaceSize = surfaceSize; - - m_viewport = viewport; - QSize size = renderTargetSize(surfaceSize); - - // Check that the returned size is before calling glViewport - if (size.isEmpty()) - return; - - // Qt3D 0------------------> 1 OpenGL 1^ - // | | - // | | - // | | - // V | - // 1 0---------------------> 1 - // The Viewport is defined between 0 and 1 which allows us to automatically - // scale to the size of the provided window surface - m_gl->functions()->glViewport(m_viewport.x() * size.width(), - (1.0 - m_viewport.y() - m_viewport.height()) * size.height(), - m_viewport.width() * size.width(), - m_viewport.height() * size.height()); -} - -void GraphicsContext::releaseOpenGL() -{ - m_shaderCache.clear(); - m_renderBufferHash.clear(); - - // Stop and destroy the OpenGL logger - if (m_debugLogger) { - m_debugLogger->stopLogging(); - m_debugLogger.reset(nullptr); - } - - qDeleteAll(m_glHelpers); -} - -// The OpenGLContext is not current on any surface at this point -void GraphicsContext::setOpenGLContext(QOpenGLContext* ctx) -{ - Q_ASSERT(ctx); - - releaseOpenGL(); - m_gl = ctx; -} - -void GraphicsContext::activateGLHelper() -{ - // Sets the correct GL Helper depending on the surface - // If no helper exists, create one - m_glHelper = m_glHelpers.value(m_surface); - if (!m_glHelper) { - m_glHelper = resolveHighestOpenGLFunctions(); - m_glHelpers.insert(m_surface, m_glHelper); - // Note: OpenGLContext is current at this point - m_gl->functions()->glDisable(GL_DITHER); - } -} - bool GraphicsContext::hasValidGLHelper() const { return m_glHelper != nullptr; @@ -510,13 +263,13 @@ void GraphicsContext::introspectShaderInterface(Shader *shader, QOpenGLShaderPro void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager) { bool wasPresent = false; - QOpenGLShaderProgram *shaderProgram = m_shaderCache.getShaderProgramAndAddRef(shader->dna(), shader->peerId(), &wasPresent); + QOpenGLShaderProgram *shaderProgram = m_shaderCache->getShaderProgramAndAddRef(shader->dna(), shader->peerId(), &wasPresent); if (!shaderProgram && !wasPresent) { // No matching QOpenGLShader in the cache so create one shaderProgram = createShaderProgram(shader); // Store in cache (even when failed and shaderProgram is null) - m_shaderCache.insert(shader->dna(), shader->peerId(), shaderProgram); + m_shaderCache->insert(shader->dna(), shader->peerId(), shaderProgram); } // Ensure the Shader node knows about the program interface @@ -524,7 +277,7 @@ void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager) // Find an already loaded shader that shares the same QOpenGLShaderProgram Shader *refShader = nullptr; - const QVector<Qt3DCore::QNodeId> sharedShaderIds = m_shaderCache.shaderIdsForProgram(shader->dna()); + const QVector<Qt3DCore::QNodeId> sharedShaderIds = m_shaderCache->shaderIdsForProgram(shader->dna()); for (const Qt3DCore::QNodeId sharedShaderId : sharedShaderIds) { Shader *sharedShader = manager->lookupResource(sharedShaderId); // Note: no need to check if shader->peerId != sharedShader->peerId @@ -548,137 +301,9 @@ void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager) } } -// Called only from RenderThread -bool GraphicsContext::activateShader(ProgramDNA shaderDNA) -{ - if (shaderDNA != m_activeShaderDNA) { - // Ensure material uniforms are re-applied - m_material = nullptr; - - m_activeShader = m_shaderCache.getShaderProgramForDNA(shaderDNA); - if (Q_LIKELY(m_activeShader != nullptr)) { - m_activeShader->bind(); - m_activeShaderDNA = shaderDNA; - } else { - m_glHelper->useProgram(0); - qWarning() << "No shader program found for DNA"; - m_activeShaderDNA = 0; - return false; - } - } - return true; -} - void GraphicsContext::removeShaderProgramReference(Shader *shaderNode) { - m_shaderCache.removeRef(shaderNode->dna(), shaderNode->peerId()); -} - -void GraphicsContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, GLuint defaultFboId) -{ - GLuint fboId = defaultFboId; // Default FBO - if (renderTargetNodeId) { - // New RenderTarget - if (!m_renderTargets.contains(renderTargetNodeId)) { - if (m_defaultFBO && fboId == m_defaultFBO) { - // this is the default fbo that some platforms create (iOS), we just register it - // Insert FBO into hash - m_renderTargets.insert(renderTargetNodeId, fboId); - } else { - fboId = createRenderTarget(renderTargetNodeId, attachments); - } - } else { - fboId = updateRenderTarget(renderTargetNodeId, attachments, true); - } - } - m_activeFBO = fboId; - m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw); - // Set active drawBuffers - activateDrawBuffers(attachments); -} - -GLuint GraphicsContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments) -{ - const GLuint fboId = m_glHelper->createFrameBufferObject(); - if (fboId) { - // The FBO is created and its attachments are set once - // Insert FBO into hash - m_renderTargets.insert(renderTargetNodeId, fboId); - // Bind FBO - m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); - bindFrameBufferAttachmentHelper(fboId, attachments); - } else { - qCritical("Failed to create FBO"); - } - return fboId; -} - -GLuint GraphicsContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget) -{ - const GLuint fboId = m_renderTargets.value(renderTargetNodeId); - - // We need to check if one of the attachment was resized - bool needsResize = !m_renderTargetsSize.contains(fboId); // not even initialized yet? - if (!needsResize) { - // render target exists, has attachment been resized? - GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager(); - const QSize s = m_renderTargetsSize[fboId]; - const auto attachments_ = attachments.attachments(); - for (const Attachment &attachment : attachments_) { - GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid); - // ### TODO QTBUG-64757 this check is insufficient since the - // texture may have changed to another one with the same size. That - // case is not handled atm. - needsResize |= (rTex != nullptr && rTex->size() != s); - if (isActiveRenderTarget) { - if (attachment.m_point == QRenderTargetOutput::Color0) - m_renderTargetFormat = rTex->properties().format; - } - } - } - - if (needsResize) { - m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); - bindFrameBufferAttachmentHelper(fboId, attachments); - } - - return fboId; -} - -void GraphicsContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments) -{ - // Set FBO attachments. These are normally textures, except that on Open GL - // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is - // desired since this cannot be achieved neither with a single texture (not - // before GLES 3.2) nor with separate textures (no suitable format for - // stencil before 3.1 with the appropriate extension). - - QSize fboSize; - GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager(); - const auto attachments_ = attachments.attachments(); - for (const Attachment &attachment : attachments_) { - GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid); - if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) { - QOpenGLTexture *glTex = rTex ? rTex->getOrCreateGLTexture() : nullptr; - if (glTex != nullptr) { - if (fboSize.isEmpty()) - fboSize = QSize(glTex->width(), glTex->height()); - else - fboSize = QSize(qMin(fboSize.width(), glTex->width()), qMin(fboSize.height(), glTex->height())); - m_glHelper->bindFrameBufferAttachment(glTex, attachment); - } - } else { - RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr; - if (renderBuffer) { - if (fboSize.isEmpty()) - fboSize = QSize(renderBuffer->width(), renderBuffer->height()); - else - fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()), qMin(fboSize.height(), renderBuffer->height())); - m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment); - } - } - } - m_renderTargetsSize.insert(fboId, fboSize); + m_shaderCache->removeRef(shaderNode->dna(), shaderNode->peerId()); } void GraphicsContext::activateDrawBuffers(const AttachmentPack &attachments) @@ -697,64 +322,6 @@ void GraphicsContext::activateDrawBuffers(const AttachmentPack &attachments) } } - -void GraphicsContext::setActiveMaterial(Material *rmat) -{ - if (m_material == rmat) - return; - - deactivateTexturesWithScope(TextureScopeMaterial); - m_material = rmat; -} - -int GraphicsContext::activateTexture(TextureScope scope, GLTexture *tex, int onUnit) -{ - // Returns the texture unit to use for the texture - // This always return a valid unit, unless there are more textures than - // texture unit available for the current material - onUnit = assignUnitForTexture(tex); - - // check we didn't overflow the available units - if (onUnit == -1) - return -1; - - // actually re-bind if required, the tex->dna on the unit not being the same - // Note: tex->dna() could be 0 if the texture has not been created yet - if (m_activeTextures[onUnit].texture != tex) { - QOpenGLTexture *glTex = tex->getOrCreateGLTexture(); - if (glTex == nullptr) - return -1; - glTex->bind(onUnit); - m_activeTextures[onUnit].texture = tex; - } - -#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) - int err = m_gl->functions()->glGetError(); - if (err) - qCWarning(Backend) << "GL error after activating texture" << QString::number(err, 16) - << tex->textureId() << "on unit" << onUnit; -#endif - - m_activeTextures[onUnit].score = 200; - m_activeTextures[onUnit].pinned = true; - m_activeTextures[onUnit].scope = scope; - - return onUnit; -} - -void GraphicsContext::deactivateTexturesWithScope(TextureScope ts) -{ - for (int u=0; u<m_activeTextures.size(); ++u) { - if (!m_activeTextures[u].pinned) - continue; // inactive, ignore - - if (m_activeTextures[u].scope == ts) { - m_activeTextures[u].pinned = false; - m_activeTextures[u].score = qMax(m_activeTextures[u].score, 1) - 1; - } - } // of units iteration -} - /*! * \internal * Finds the highest supported opengl version and internally use the most optimized @@ -843,34 +410,6 @@ GraphicsHelperInterface *GraphicsContext::resolveHighestOpenGLFunctions() return glHelper; } -void GraphicsContext::deactivateTexture(GLTexture* tex) -{ - for (int u=0; u<m_activeTextures.size(); ++u) { - if (m_activeTextures[u].texture == tex) { - Q_ASSERT(m_activeTextures[u].pinned); - m_activeTextures[u].pinned = false; - return; - } - } // of units iteration - - qCWarning(Backend) << Q_FUNC_INFO << "texture not active:" << tex; -} - -void GraphicsContext::setCurrentStateSet(RenderStateSet *ss) -{ - if (ss == m_stateSet) - return; - - if (ss) - ss->apply(this); - m_stateSet = ss; -} - -RenderStateSet *GraphicsContext::currentStateSet() const -{ - return m_stateSet; -} - const GraphicsApiFilterData *GraphicsContext::contextInfo() const { return &m_contextInfo; @@ -1064,26 +603,17 @@ GLuint GraphicsContext::boundFrameBufferObject() void GraphicsContext::clearColor(const QColor &color) { - if (m_currClearColorValue != color) { - m_currClearColorValue = color; - m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); - } + m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); } void GraphicsContext::clearDepthValue(float depth) { - if (m_currClearDepthValue != depth) { - m_currClearDepthValue = depth; - m_gl->functions()->glClearDepthf(depth); - } + m_gl->functions()->glClearDepthf(depth); } void GraphicsContext::clearStencilValue(int stencil) { - if (m_currClearStencilValue != stencil) { - m_currClearStencilValue = stencil; - m_gl->functions()->glClearStencil(stencil); - } + m_gl->functions()->glClearStencil(stencil); } void GraphicsContext::enableClipPlane(int clipPlane) @@ -1106,6 +636,11 @@ GLint GraphicsContext::maxClipPlaneCount() return m_glHelper->maxClipPlaneCount(); } +GLint GraphicsContext::maxTextureUnitsCount() +{ + return m_maxTextureUnits; +} + void GraphicsContext::enablePrimitiveRestart(int restartIndex) { if (m_glHelper->supportsFeature(GraphicsHelperInterface::PrimitiveRestart)) @@ -1154,140 +689,6 @@ void GraphicsContext::setSeamlessCubemap(bool enable) m_glHelper->setSeamlessCubemap(enable); } -/*! - \internal - Returns a texture unit for a texture, -1 if all texture units are assigned. - Tries to use the texture unit with the texture that hasn't been used for the longest time - if the texture happens not to be already pinned on a texture unit. - */ -GLint GraphicsContext::assignUnitForTexture(GLTexture *tex) -{ - int lowestScoredUnit = -1; - int lowestScore = 0xfffffff; - - for (int u=0; u<m_activeTextures.size(); ++u) { - if (m_activeTextures[u].texture == tex) - return u; - - // No texture is currently active on the texture unit - // we save the texture unit with the texture that has been on there - // the longest time while not being used - if (!m_activeTextures[u].pinned) { - int score = m_activeTextures[u].score; - if (score < lowestScore) { - lowestScore = score; - lowestScoredUnit = u; - } - } - } // of units iteration - - if (lowestScoredUnit == -1) - qCWarning(Backend) << Q_FUNC_INFO << "No free texture units!"; - - return lowestScoredUnit; -} - -void GraphicsContext::decayTextureScores() -{ - for (int u = 0; u < m_activeTextures.size(); u++) - m_activeTextures[u].score = qMax(m_activeTextures[u].score - 1, 0); -} - -QOpenGLShaderProgram* GraphicsContext::activeShader() const -{ - Q_ASSERT(m_activeShader); - return m_activeShader; -} - -void GraphicsContext::setRenderer(Renderer *renderer) -{ - m_renderer = renderer; -} - -// It will be easier if the QGraphicContext applies the QUniformPack -// than the other way around -bool GraphicsContext::setParameters(ShaderParameterPack ¶meterPack) -{ - // Activate textures and update TextureUniform in the pack - // with the correct textureUnit - - // Set the pinned texture of the previous material texture - // to pinable so that we should easily find an available texture unit - NodeManagers *manager = m_renderer->nodeManagers(); - - deactivateTexturesWithScope(TextureScopeMaterial); - // Update the uniforms with the correct texture unit id's - PackUniformHash &uniformValues = parameterPack.uniforms(); - - for (int i = 0; i < parameterPack.textures().size(); ++i) { - const ShaderParameterPack::NamedTexture &namedTex = parameterPack.textures().at(i); - // Given a Texture QNodeId, we retrieve the associated shared GLTexture - if (uniformValues.contains(namedTex.glslNameId)) { - GLTexture *t = manager->glTextureManager()->lookupResource(namedTex.texId); - if (t != nullptr) { - UniformValue &texUniform = uniformValues[namedTex.glslNameId]; - Q_ASSERT(texUniform.valueType() == UniformValue::TextureValue); - const int texUnit = activateTexture(TextureScopeMaterial, t); - texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit; - if (texUnit == -1) - return false; - } - } - } - - QOpenGLShaderProgram *shader = activeShader(); - - // TO DO: We could cache the binding points somehow and only do the binding when necessary - // for SSBO and UBO - - // Bind Shader Storage block to SSBO and update SSBO - const QVector<BlockToSSBO> blockToSSBOs = parameterPack.shaderStorageBuffers(); - int ssboIndex = 0; - for (const BlockToSSBO b : blockToSSBOs) { - Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); - GLBuffer *ssbo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::ShaderStorageBuffer); - bindShaderStorageBlock(shader->programId(), b.m_blockIndex, ssboIndex); - // Needed to avoid conflict where the buffer would already - // be bound as a VertexArray - bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer); - ssbo->bindBufferBase(this, ssboIndex++, GLBuffer::ShaderStorageBuffer); - // TO DO: Make sure that there's enough binding points - } - - // Bind UniformBlocks to UBO and update UBO from Buffer - // TO DO: Convert ShaderData to Buffer so that we can use that generic process - const QVector<BlockToUBO> blockToUBOs = parameterPack.uniformBuffers(); - int uboIndex = 0; - for (const BlockToUBO &b : blockToUBOs) { - Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); - GLBuffer *ubo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::UniformBuffer); - bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex); - // Needed to avoid conflict where the buffer would already - // be bound as a VertexArray - bindGLBuffer(ubo, GLBuffer::UniformBuffer); - ubo->bindBufferBase(this, uboIndex++, GLBuffer::UniformBuffer); - // TO DO: Make sure that there's enough binding points - } - - // Update uniforms in the Default Uniform Block - const PackUniformHash values = parameterPack.uniforms(); - const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms(); - - for (const ShaderUniform &uniform : activeUniforms) { - // We can use [] as we are sure the the uniform wouldn't - // be un activeUniforms if there wasn't a matching value - const UniformValue &v = values[uniform.m_nameId]; - - // skip invalid textures - if (v.valueType() == UniformValue::TextureValue && *v.constData<int>() == -1) - continue; - - applyUniform(uniform, v); - } - // if not all data is valid, the next frame will be rendered immediately - return true; -} - void GraphicsContext::readBuffer(GLenum mode) { m_glHelper->readBuffer(mode); @@ -1298,36 +699,6 @@ void GraphicsContext::drawBuffer(GLenum mode) m_glHelper->drawBuffer(mode); } -void GraphicsContext::enableAttribute(const VAOVertexAttribute &attr) -{ - // Bind buffer within the current VAO - GLBuffer *buf = m_renderer->nodeManagers()->glBufferManager()->data(attr.bufferHandle); - Q_ASSERT(buf); - bindGLBuffer(buf, attr.attributeType); - - // Don't use QOpenGLShaderProgram::setAttributeBuffer() because of QTBUG-43199. - // Use the introspection data and set the attribute explicitly - m_glHelper->enableVertexAttributeArray(attr.location); - m_glHelper->vertexAttributePointer(attr.shaderDataType, - attr.location, - attr.vertexSize, - attr.dataType, - GL_TRUE, // TODO: Support normalization property on QAttribute - attr.byteStride, - reinterpret_cast<const void *>(qintptr(attr.byteOffset))); - - - // Done by the helper if it supports it - if (attr.divisor != 0) - m_glHelper->vertexAttribDivisor(attr.location, attr.divisor); -} - -void GraphicsContext::disableAttribute(const GraphicsContext::VAOVertexAttribute &attr) -{ - QOpenGLShaderProgram *prog = activeShader(); - prog->disableAttributeArray(attr.location); -} - void GraphicsContext::applyUniform(const ShaderUniform &description, const UniformValue &v) { const UniformType type = m_glHelper->uniformTypeFromGLType(description.m_type); @@ -1439,282 +810,11 @@ void GraphicsContext::applyUniform(const ShaderUniform &description, const Unifo } } -// Note: needs to be called while VAO is bound -void GraphicsContext::specifyAttribute(const Attribute *attribute, - Buffer *buffer, - const ShaderAttribute *attributeDescription) -{ - const int location = attributeDescription->m_location; - if (location < 0) { - qCWarning(Backend) << "failed to resolve location for attribute:" << attribute->name(); - return; - } - - const GLint attributeDataType = glDataTypeFromAttributeDataType(attribute->vertexBaseType()); - const HGLBuffer glBufferHandle = m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()); - Q_ASSERT(!glBufferHandle.isNull()); - const GLBuffer::Type attributeType = attributeTypeToGLBufferType(attribute->attributeType()); - - int typeSize = 0; - int attrCount = 0; - - if (attribute->vertexSize() >= 1 && attribute->vertexSize() <= 4) { - attrCount = 1; - } else if (attribute->vertexSize() == 9) { - typeSize = byteSizeFromType(attributeDataType); - attrCount = 3; - } else if (attribute->vertexSize() == 16) { - typeSize = byteSizeFromType(attributeDataType); - attrCount = 4; - } else { - Q_UNREACHABLE(); - } - - for (int i = 0; i < attrCount; i++) { - VAOVertexAttribute attr; - attr.bufferHandle = glBufferHandle; - attr.attributeType = attributeType; - attr.location = location + i; - attr.dataType = attributeDataType; - attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize); - attr.vertexSize = attribute->vertexSize() / attrCount; - attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize); - attr.divisor = attribute->divisor(); - attr.shaderDataType = attributeDescription->m_type; - - enableAttribute(attr); - - // Save this in the current emulated VAO - if (m_currentVAO) - m_currentVAO->saveVertexAttribute(attr); - } -} - -void GraphicsContext::specifyIndices(Buffer *buffer) -{ - GLBuffer *buf = glBufferForRenderBuffer(buffer, GLBuffer::IndexBuffer); - if (!bindGLBuffer(buf, GLBuffer::IndexBuffer)) - qCWarning(Backend) << Q_FUNC_INFO << "binding index buffer failed"; - - // bound within the current VAO - // Save this in the current emulated VAO - if (m_currentVAO) - m_currentVAO->saveIndexAttribute(m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId())); -} - -void GraphicsContext::updateBuffer(Buffer *buffer) -{ - const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); - if (it != m_renderBufferHash.end()) - uploadDataToGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value())); -} - -QByteArray GraphicsContext::downloadBufferContent(Buffer *buffer) -{ - const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); - if (it != m_renderBufferHash.end()) - return downloadDataFromGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value())); - return QByteArray(); -} - - - -void GraphicsContext::releaseBuffer(Qt3DCore::QNodeId bufferId) -{ - auto it = m_renderBufferHash.find(bufferId); - if (it != m_renderBufferHash.end()) { - HGLBuffer glBuffHandle = it.value(); - GLBuffer *glBuff = m_renderer->nodeManagers()->glBufferManager()->data(glBuffHandle); - - Q_ASSERT(glBuff); - // Destroy the GPU resource - glBuff->destroy(this); - // Destroy the GLBuffer instance - m_renderer->nodeManagers()->glBufferManager()->releaseResource(bufferId); - // Remove Id - HGLBuffer entry - m_renderBufferHash.erase(it); - } -} - -bool GraphicsContext::hasGLBufferForBuffer(Buffer *buffer) -{ - const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); - return (it != m_renderBufferHash.end()); -} - -void GraphicsContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId, - Qt3DCore::QNodeId outputRenderTargetId, - QRect inputRect, QRect outputRect, - uint defaultFboId, - QRenderTargetOutput::AttachmentPoint inputAttachmentPoint, - QRenderTargetOutput::AttachmentPoint outputAttachmentPoint, - QBlitFramebuffer::InterpolationMethod interpolationMethod) -{ - GLuint inputFboId = defaultFboId; - bool inputBufferIsDefault = true; - if (!inputRenderTargetId.isNull()) { - RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(inputRenderTargetId); - if (renderTarget) { - AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager()); - if (m_renderTargets.contains(inputRenderTargetId)) - inputFboId = updateRenderTarget(inputRenderTargetId, attachments, false); - else - inputFboId = createRenderTarget(inputRenderTargetId, attachments); - } - inputBufferIsDefault = false; - } - - GLuint outputFboId = defaultFboId; - bool outputBufferIsDefault = true; - if (!outputRenderTargetId.isNull()) { - RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(outputRenderTargetId); - if (renderTarget) { - AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager()); - if (m_renderTargets.contains(outputRenderTargetId)) - outputFboId = updateRenderTarget(outputRenderTargetId, attachments, false); - else - outputFboId = createRenderTarget(outputRenderTargetId, attachments); - } - outputBufferIsDefault = false; - } - - // Up until this point the input and output rects are normal Qt rectangles. - // Convert them to GL rectangles (Y at bottom). - const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[inputFboId].height(); - const GLint srcX0 = inputRect.left(); - const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height()); - const GLint srcX1 = srcX0 + inputRect.width(); - const GLint srcY1 = srcY0 + inputRect.height(); - - const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[outputFboId].height(); - const GLint dstX0 = outputRect.left(); - const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height()); - const GLint dstX1 = dstX0 + outputRect.width(); - const GLint dstY1 = dstY0 + outputRect.height(); - - //Get the last bounded framebuffers - const GLuint lastDrawFboId = boundFrameBufferObject(); - - // Activate input framebuffer for reading - bindFramebuffer(inputFboId, GraphicsHelperInterface::FBORead); - - // Activate output framebuffer for writing - bindFramebuffer(outputFboId, GraphicsHelperInterface::FBODraw); - - //Bind texture - if (!inputBufferIsDefault) - readBuffer(GL_COLOR_ATTACHMENT0 + inputAttachmentPoint); - - if (!outputBufferIsDefault) - drawBuffer(GL_COLOR_ATTACHMENT0 + outputAttachmentPoint); - - // Blit framebuffer - const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR; - m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, - GL_COLOR_BUFFER_BIT, mode); - - // Reset draw buffer - bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw); -} - void GraphicsContext::memoryBarrier(QMemoryBarrier::Operations barriers) { m_glHelper->memoryBarrier(barriers); } -GLBuffer *GraphicsContext::glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type) -{ - if (!m_renderBufferHash.contains(buf->peerId())) - m_renderBufferHash.insert(buf->peerId(), createGLBufferFor(buf, type)); - return m_renderer->nodeManagers()->glBufferManager()->data(m_renderBufferHash.value(buf->peerId())); -} - -HGLBuffer GraphicsContext::createGLBufferFor(Buffer *buffer, GLBuffer::Type type) -{ - GLBuffer *b = m_renderer->nodeManagers()->glBufferManager()->getOrCreateResource(buffer->peerId()); - // b.setUsagePattern(static_cast<QOpenGLBuffer::UsagePattern>(buffer->usage())); - Q_ASSERT(b); - if (!b->create(this)) - qCWarning(Render::Io) << Q_FUNC_INFO << "buffer creation failed"; - - if (!bindGLBuffer(b, type)) - qCWarning(Render::Io) << Q_FUNC_INFO << "buffer binding failed"; - - return m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()); -} - -bool GraphicsContext::bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type) -{ - if (type == GLBuffer::ArrayBuffer && buffer == m_boundArrayBuffer) - return true; - - if (buffer->bind(this, type)) { - if (type == GLBuffer::ArrayBuffer) - m_boundArrayBuffer = buffer; - return true; - } - return false; -} - -void GraphicsContext::uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer) -{ - if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're uploading, the type doesn't matter here - qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed"; - // If the buffer is dirty (hence being called here) - // there are two possible cases - // * setData was called changing the whole data or functor (or the usage pattern) - // * partial buffer updates where received - - // TO DO: Handle usage pattern - QVector<Qt3DRender::QBufferUpdate> updates = std::move(buffer->pendingBufferUpdates()); - for (auto it = updates.begin(); it != updates.end(); ++it) { - auto update = it; - // We have a partial update - if (update->offset >= 0) { - //accumulate sequential updates as single one - int bufferSize = update->data.size(); - auto it2 = it + 1; - while ((it2 != updates.end()) - && (it2->offset - update->offset == bufferSize)) { - bufferSize += it2->data.size(); - ++it2; - } - update->data.resize(bufferSize); - while (it + 1 != it2) { - ++it; - update->data.replace(it->offset - update->offset, it->data.size(), it->data); - it->data.clear(); - } - // TO DO: based on the number of updates .., it might make sense to - // sometime use glMapBuffer rather than glBufferSubData - b->update(this, update->data.constData(), update->data.size(), update->offset); - } else { - // We have an update that was done by calling QBuffer::setData - // which is used to resize or entirely clear the buffer - // Note: we use the buffer data directly in that case - const int bufferSize = buffer->data().size(); - b->allocate(this, bufferSize, false); // orphan the buffer - b->allocate(this, buffer->data().constData(), bufferSize, false); - } - } - - if (releaseBuffer) { - b->release(this); - m_boundArrayBuffer = nullptr; - } - qCDebug(Render::Io) << "uploaded buffer size=" << buffer->data().size(); -} - -QByteArray GraphicsContext::downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b) -{ - if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here - qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed"; - - QByteArray data = b->download(this, buffer->data().size()); - return data; -} - GLint GraphicsContext::elementType(GLint type) { switch (type) { @@ -1832,173 +932,6 @@ GLint GraphicsContext::glDataTypeFromAttributeDataType(QAttribute::VertexBaseTyp return GL_FLOAT; } -static void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width, uint height, QAbstractTexture::TextureFormat format) -{ - switch (format) { - case QAbstractTexture::RGBA32F: - { - uchar *srcScanline = (uchar *)srcData + stride * (height - 1); - for (uint i = 0; i < height; ++i) { - uchar *dstScanline = img.scanLine(i); - float *pSrc = (float*)srcScanline; - for (uint j = 0; j < width; j++) { - *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+2], 1.0f)); - *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+1], 1.0f)); - *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+0], 1.0f)); - *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+3], 1.0f)); - } - srcScanline -= stride; - } - } break; - default: - { - uchar* srcScanline = (uchar *)srcData + stride * (height - 1); - for (uint i = 0; i < height; ++i) { - memcpy(img.scanLine(i), srcScanline, stride); - srcScanline -= stride; - } - } break; - } -} - -QImage GraphicsContext::readFramebuffer(const QRect &rect) -{ - QImage img; - const unsigned int area = rect.width() * rect.height(); - unsigned int bytes; - GLenum format, type; - QImage::Format imageFormat; - uint stride; - - /* format value should match GL internalFormat */ - GLenum internalFormat = m_renderTargetFormat; - - switch (m_renderTargetFormat) { - case QAbstractTexture::RGBAFormat: - case QAbstractTexture::RGBA8_SNorm: - case QAbstractTexture::RGBA8_UNorm: - case QAbstractTexture::RGBA8U: - case QAbstractTexture::SRGB8_Alpha8: -#ifdef QT_OPENGL_ES_2 - format = GL_RGBA; - imageFormat = QImage::Format_RGBA8888_Premultiplied; -#else - format = GL_BGRA; - imageFormat = QImage::Format_ARGB32_Premultiplied; - internalFormat = GL_RGBA8; -#endif - type = GL_UNSIGNED_BYTE; - bytes = area * 4; - stride = rect.width() * 4; - break; - case QAbstractTexture::SRGB8: - case QAbstractTexture::RGBFormat: - case QAbstractTexture::RGB8U: - case QAbstractTexture::RGB8_UNorm: -#ifdef QT_OPENGL_ES_2 - format = GL_RGBA; - imageFormat = QImage::Format_RGBX8888; -#else - format = GL_BGRA; - imageFormat = QImage::Format_RGB32; - internalFormat = GL_RGB8; -#endif - type = GL_UNSIGNED_BYTE; - bytes = area * 4; - stride = rect.width() * 4; - break; -#ifndef QT_OPENGL_ES_2 - case QAbstractTexture::RG11B10F: - bytes = area * 4; - format = GL_RGB; - type = GL_UNSIGNED_INT_10F_11F_11F_REV; - imageFormat = QImage::Format_RGB30; - stride = rect.width() * 4; - break; - case QAbstractTexture::RGB10A2: - bytes = area * 4; - format = GL_RGBA; - type = GL_UNSIGNED_INT_2_10_10_10_REV; - imageFormat = QImage::Format_A2BGR30_Premultiplied; - stride = rect.width() * 4; - break; - case QAbstractTexture::R5G6B5: - bytes = area * 2; - format = GL_RGB; - type = GL_UNSIGNED_SHORT; - internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV; - imageFormat = QImage::Format_RGB16; - stride = rect.width() * 2; - break; - case QAbstractTexture::RGBA16F: - case QAbstractTexture::RGBA16U: - case QAbstractTexture::RGBA32F: - case QAbstractTexture::RGBA32U: - bytes = area * 16; - format = GL_RGBA; - type = GL_FLOAT; - imageFormat = QImage::Format_ARGB32_Premultiplied; - stride = rect.width() * 16; - break; -#endif - default: - // unsupported format - Q_UNREACHABLE(); - return img; - } - - GLint samples = 0; - m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples); - if (samples > 0 && !m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) { - qWarning () << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; " - "Required feature BlitFramebuffer is missing."; - return img; - } - - img = QImage(rect.width(), rect.height(), imageFormat); - - QScopedArrayPointer<uchar> data(new uchar [bytes]); - - if (samples > 0) { - // resolve multisample-framebuffer to renderbuffer and read pixels from it - GLuint fbo, rb; - QOpenGLFunctions *gl = m_gl->functions(); - gl->glGenFramebuffers(1, &fbo); - gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); - gl->glGenRenderbuffers(1, &rb); - gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); - gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(), rect.height()); - gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); - - const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - gl->glDeleteRenderbuffers(1, &rb); - gl->glDeleteFramebuffers(1, &fbo); - qWarning () << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status; - return img; - } - - m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(), - 0, 0, rect.width(), rect.height(), - GL_COLOR_BUFFER_BIT, GL_NEAREST); - gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data()); - - copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); - - gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); - gl->glDeleteRenderbuffers(1, &rb); - gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO); - gl->glDeleteFramebuffers(1, &fbo); - } else { - // read pixels directly from framebuffer - m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), format, type, data.data()); - copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); - } - - return img; -} - QT3D_UNIFORM_TYPE_IMPL(UniformType::Float, float, glUniform1fv) QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec2, float, glUniform2fv) QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec3, float, glUniform3fv) diff --git a/src/render/graphicshelpers/graphicscontext_p.h b/src/render/graphicshelpers/graphicscontext_p.h index 24b08e45e..82db57433 100644 --- a/src/render/graphicshelpers/graphicscontext_p.h +++ b/src/render/graphicshelpers/graphicscontext_p.h @@ -76,33 +76,19 @@ QT_BEGIN_NAMESPACE -class QOpenGLDebugLogger; class QOpenGLShaderProgram; class QAbstractOpenGLFunctions; +class QOpenGLDebugLogger; namespace Qt3DRender { namespace Render { -class Renderer; class GraphicsHelperInterface; -class RenderStateSet; -class Material; -class GLTexture; -class RenderCommand; class RenderTarget; class AttachmentPack; -class Attribute; -class Buffer; class ShaderManager; -enum TextureScope -{ - TextureScopeMaterial = 0, - TextureScopeTechnique - // per-pass for deferred rendering? -}; - typedef QPair<QString, int> NamedUniformLocation; class Q_AUTOTEST_EXPORT GraphicsContext @@ -111,100 +97,28 @@ public: GraphicsContext(); ~GraphicsContext(); - int id() const; // unique, small integer ID of this context - - bool beginDrawing(QSurface *surface); - void clearBackBuffer(QClearBuffers::BufferTypeFlags buffers); - void endDrawing(bool swapBuffers); + void setShaderCache(ShaderCache *shaderCache) { m_shaderCache = shaderCache; } + ShaderCache *shaderCache() const { return m_shaderCache; } - void setViewport(const QRectF &viewport, const QSize &surfaceSize); - QRectF viewport() const { return m_viewport; } - - /** - * @brief releaseGL - release all OpenGL objects associated with - * this context - */ - void releaseOpenGL(); void setOpenGLContext(QOpenGLContext* ctx); QOpenGLContext *openGLContext() { return m_gl; } bool makeCurrent(QSurface *surface); void doneCurrent(); - void activateGLHelper(); bool hasValidGLHelper() const; bool isInitialized() const; + // Shaders QOpenGLShaderProgram *createShaderProgram(Shader *shaderNode); + void introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram); void loadShader(Shader* shader, ShaderManager *manager); - bool activateShader(ProgramDNA shaderDNA); void removeShaderProgramReference(Shader *shaderNode); - void introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram); - GLuint activeFBO() const { return m_activeFBO; } GLuint defaultFBO() const { return m_defaultFBO; } - void activateRenderTarget(const Qt3DCore::QNodeId id, const AttachmentPack &attachments, GLuint defaultFboId); - - Material* activeMaterial() const { return m_material; } - - void setActiveMaterial(Material* rmat); - - void executeCommand(const RenderCommand *command); - - QSize renderTargetSize(const QSize &surfaceSize) const; - - /** - * @brief activeShader - * @return - */ - QOpenGLShaderProgram* activeShader() const; - - void setRenderer(Renderer *renderer); - - void specifyAttribute(const Attribute *attribute, - Buffer *buffer, - const ShaderAttribute *attributeDescription); - void specifyIndices(Buffer *buffer); - void updateBuffer(Buffer *buffer); - QByteArray downloadBufferContent(Buffer *buffer); - void releaseBuffer(Qt3DCore::QNodeId bufferId); - bool hasGLBufferForBuffer(Buffer *buffer); - - void blitFramebuffer(Qt3DCore::QNodeId outputRenderTargetId, Qt3DCore::QNodeId inputRenderTargetId, - QRect inputRect, - QRect outputRect, uint defaultFboId, - QRenderTargetOutput::AttachmentPoint inputAttachmentPoint, - QRenderTargetOutput::AttachmentPoint outputAttachmentPoint, - QBlitFramebuffer::InterpolationMethod interpolationMethod); - - void memoryBarrier(QMemoryBarrier::Operations barriers); - - bool setParameters(ShaderParameterPack ¶meterPack); - - void readBuffer(GLenum mode); - void drawBuffer(GLenum mode); - - /** - * @brief glBufferFor - given a client-side (CPU) buffer, provide the - * context-specific object. Initial call will create the buffer. - * @param buf - * @return - */ - GLBuffer *glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type); - /** - * @brief activateTexture - make a texture active on a hardware unit - * @param tex - the texture to activate - * @param onUnit - option, specify the explicit unit to activate on - * @return - the unit the texture was activated on - */ - int activateTexture(TextureScope scope, GLTexture* tex, int onUnit = -1); - - void deactivateTexture(GLTexture *tex); - - void setCurrentStateSet(RenderStateSet* ss); - RenderStateSet *currentStateSet() const; const GraphicsApiFilterData *contextInfo() const; // Wrapper methods + void clearBackBuffer(QClearBuffers::BufferTypeFlags buffers); void alphaTest(GLenum mode1, GLenum mode2); void bindFramebuffer(GLuint fbo, GraphicsHelperInterface::FBOBindMode mode); void bindBufferBase(GLenum target, GLuint bindingIndex, GLuint buffer); @@ -240,12 +154,17 @@ public: void enablePrimitiveRestart(int restartIndex); void frontFace(GLenum mode); GLint maxClipPlaneCount(); + GLint maxTextureUnitsCount(); void pointSize(bool programmable, GLfloat value); + void readBuffer(GLenum mode); + void drawBuffer(GLenum mode); void setMSAAEnabled(bool enabled); void setAlphaCoverageEnabled(bool enabled); void setClipPlane(int clipPlane, const QVector3D &normal, float distance); void setSeamlessCubemap(bool enable); void setVerticesPerPatch(GLint verticesPerPatch); + void memoryBarrier(QMemoryBarrier::Operations barriers); + void activateDrawBuffers(const AttachmentPack &attachments); // Helper methods static GLint elementType(GLint type); @@ -256,99 +175,24 @@ public: bool supportsDrawBuffersBlend() const; bool supportsVAO() const { return m_supportsVAO; } - QImage readFramebuffer(const QRect &rect); - -private: void initialize(); - - void decayTextureScores(); - - GLint assignUnitForTexture(GLTexture* tex); - void deactivateTexturesWithScope(TextureScope ts); - GraphicsHelperInterface *resolveHighestOpenGLFunctions(); - GLuint createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments); - GLuint updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget); - void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments); - void activateDrawBuffers(const AttachmentPack &attachments); - HGLBuffer createGLBufferFor(Buffer *buffer, GLBuffer::Type type); - void uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer = false); - QByteArray downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b); - bool bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type); - void resolveRenderTargetFormat(); - bool m_initialized; - const unsigned int m_id; + bool m_supportsVAO; + GLint m_maxTextureUnits; + GLuint m_defaultFBO; QOpenGLContext *m_gl; - QSurface *m_surface; GraphicsHelperInterface *m_glHelper; - bool m_ownCurrent; - - ShaderCache m_shaderCache; - QOpenGLShaderProgram *m_activeShader; - ProgramDNA m_activeShaderDNA; - - QHash<Qt3DCore::QNodeId, HGLBuffer> m_renderBufferHash; - QHash<Qt3DCore::QNodeId, GLuint> m_renderTargets; - QHash<GLuint, QSize> m_renderTargetsSize; - QAbstractTexture::TextureFormat m_renderTargetFormat; - QSize m_surfaceSize; QHash<QSurface *, GraphicsHelperInterface*> m_glHelpers; - - // active textures, indexed by texture unit - struct ActiveTexture { - GLTexture *texture = nullptr; - int score = 0; - TextureScope scope = TextureScopeMaterial; - bool pinned = false; - }; - QVector<ActiveTexture> m_activeTextures; - - // cache some current state, to make sure we don't issue unnecessary GL calls - int m_currClearStencilValue; - float m_currClearDepthValue; - QColor m_currClearColorValue; - - Material* m_material; - QRectF m_viewport; - GLuint m_activeFBO; - GLuint m_defaultFBO; - - GLBuffer *m_boundArrayBuffer; - - RenderStateSet* m_stateSet; - - Renderer *m_renderer; GraphicsApiFilterData m_contextInfo; - - QByteArray m_uboTempArray; - - bool m_supportsVAO; + ShaderCache *m_shaderCache; QScopedPointer<QOpenGLDebugLogger> m_debugLogger; friend class OpenGLVertexArrayObject; OpenGLVertexArrayObject *m_currentVAO; - struct VAOVertexAttribute - { - HGLBuffer bufferHandle; - GLBuffer::Type attributeType; - int location; - GLint dataType; - uint byteOffset; - uint vertexSize; - uint byteStride; - uint divisor; - GLenum shaderDataType; - }; - - using VAOIndexAttribute = HGLBuffer; - - void enableAttribute(const VAOVertexAttribute &attr); - void disableAttribute(const VAOVertexAttribute &attr); - void applyUniform(const ShaderUniform &description, const UniformValue &v); template<UniformType> diff --git a/src/render/graphicshelpers/graphicshelpers.pri b/src/render/graphicshelpers/graphicshelpers.pri index ff529ba44..b3698858a 100644 --- a/src/render/graphicshelpers/graphicshelpers.pri +++ b/src/render/graphicshelpers/graphicshelpers.pri @@ -11,7 +11,8 @@ HEADERS += \ $$PWD/graphicshelpergl2_p.h \ $$PWD/graphicshelpergl3_3_p.h \ $$PWD/graphicshelpergl4_p.h \ - $$PWD/graphicshelpergl3_2_p.h + $$PWD/graphicshelpergl3_2_p.h \ + $$PWD/submissioncontext_p.h SOURCES += \ $$PWD/graphicscontext.cpp \ @@ -21,4 +22,5 @@ SOURCES += \ $$PWD/graphicshelpergl2.cpp \ $$PWD/graphicshelpergl3_3.cpp \ $$PWD/graphicshelpergl4.cpp \ - $$PWD/graphicshelpergl3_2.cpp + $$PWD/graphicshelpergl3_2.cpp \ + $$PWD/submissioncontext.cpp diff --git a/src/render/graphicshelpers/submissioncontext.cpp b/src/render/graphicshelpers/submissioncontext.cpp new file mode 100644 index 000000000..df7d61902 --- /dev/null +++ b/src/render/graphicshelpers/submissioncontext.cpp @@ -0,0 +1,1211 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "submissioncontext_p.h" + +#include <Qt3DRender/qgraphicsapifilter.h> +#include <Qt3DRender/qparameter.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/shader_p.h> +#include <Qt3DRender/private/material_p.h> +#include <Qt3DRender/private/gltexture_p.h> +#include <Qt3DRender/private/buffer_p.h> +#include <Qt3DRender/private/attribute_p.h> +#include <Qt3DRender/private/rendercommand_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <Qt3DRender/private/rendertarget_p.h> +#include <Qt3DRender/private/graphicshelperinterface_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/gltexturemanager_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> +#include <Qt3DRender/private/qbuffer_p.h> +#include <Qt3DRender/private/renderbuffer_p.h> +#include <QOpenGLShaderProgram> + +#if !defined(QT_OPENGL_ES_2) +#include <QOpenGLFunctions_2_0> +#include <QOpenGLFunctions_3_2_Core> +#include <QOpenGLFunctions_3_3_Core> +#include <QOpenGLFunctions_4_3_Core> +#include <Qt3DRender/private/graphicshelpergl2_p.h> +#include <Qt3DRender/private/graphicshelpergl3_2_p.h> +#include <Qt3DRender/private/graphicshelpergl3_3_p.h> +#include <Qt3DRender/private/graphicshelpergl4_p.h> +#endif +#include <Qt3DRender/private/graphicshelperes2_p.h> +#include <Qt3DRender/private/graphicshelperes3_p.h> + +#include <QSurface> +#include <QWindow> +#include <QOpenGLTexture> +#include <QOpenGLDebugLogger> + +QT_BEGIN_NAMESPACE + +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif + +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif + +namespace Qt3DRender { +namespace Render { + +static QHash<unsigned int, SubmissionContext*> static_contexts; + +namespace { + +GLBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type) +{ + switch (type) { + case QAttribute::VertexAttribute: + return GLBuffer::ArrayBuffer; + case QAttribute::IndexAttribute: + return GLBuffer::IndexBuffer; + case QAttribute::DrawIndirectAttribute: + return GLBuffer::DrawIndirectBuffer; + default: + Q_UNREACHABLE(); + } +} + +void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width, uint height, QAbstractTexture::TextureFormat format) +{ + switch (format) { + case QAbstractTexture::RGBA32F: + { + uchar *srcScanline = (uchar *)srcData + stride * (height - 1); + for (uint i = 0; i < height; ++i) { + uchar *dstScanline = img.scanLine(i); + float *pSrc = (float*)srcScanline; + for (uint j = 0; j < width; j++) { + *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+2], 1.0f)); + *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+1], 1.0f)); + *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+0], 1.0f)); + *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+3], 1.0f)); + } + srcScanline -= stride; + } + } break; + default: + { + uchar* srcScanline = (uchar *)srcData + stride * (height - 1); + for (uint i = 0; i < height; ++i) { + memcpy(img.scanLine(i), srcScanline, stride); + srcScanline -= stride; + } + } break; + } +} + +} // anonymous + +unsigned int nextFreeContextId() +{ + for (unsigned int i=0; i < 0xffff; ++i) { + if (!static_contexts.contains(i)) + return i; + } + + qFatal("Couldn't find free context ID"); + return 0; +} + +SubmissionContext::SubmissionContext() + : GraphicsContext() + , m_initialized(false) + , m_ownCurrent(true) + , m_id(nextFreeContextId()) + , m_surface(nullptr) + , m_activeShader(nullptr) + , m_activeShaderDNA(0) + , m_renderTargetFormat(QAbstractTexture::NoFormat) + , m_currClearStencilValue(0) + , m_currClearDepthValue(1.f) + , m_currClearColorValue(0,0,0,0) + , m_material(nullptr) + , m_activeFBO(0) + , m_boundArrayBuffer(nullptr) + , m_stateSet(nullptr) + , m_renderer(nullptr) + , m_uboTempArray(QByteArray(1024, 0)) + , m_currentVAO(nullptr) +{ + static_contexts[m_id] = this; +} + +SubmissionContext::~SubmissionContext() +{ + releaseOpenGL(); + + Q_ASSERT(static_contexts[m_id] == this); + static_contexts.remove(m_id); +} + +void SubmissionContext::initialize() +{ + GraphicsContext::initialize(); + m_activeTextures.resize(maxTextureUnitsCount()); +} + +void SubmissionContext::resolveRenderTargetFormat() +{ + const QSurfaceFormat format = m_gl->format(); + const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize(); + const uint r = format.redBufferSize(); + const uint g = format.greenBufferSize(); + const uint b = format.blueBufferSize(); + +#define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18)) + + const uint bits = RGBA_BITS(r,g,b,a); + switch (bits) { + case RGBA_BITS(8,8,8,8): + m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm; + break; + case RGBA_BITS(8,8,8,0): + m_renderTargetFormat = QAbstractTexture::RGB8_UNorm; + break; + case RGBA_BITS(5,6,5,0): + m_renderTargetFormat = QAbstractTexture::R5G6B5; + break; + } +#undef RGBA_BITS +} + +bool SubmissionContext::beginDrawing(QSurface *surface) +{ + Q_ASSERT(surface); + Q_ASSERT(m_gl); + + m_surface = surface; + + // TO DO: Find a way to make to pause work if the window is not exposed + // if (m_surface && m_surface->surfaceClass() == QSurface::Window) { + // qDebug() << Q_FUNC_INFO << 1; + // if (!static_cast<QWindow *>(m_surface)->isExposed()) + // return false; + // qDebug() << Q_FUNC_INFO << 2; + // } + + // Makes the surface current on the OpenGLContext + // and sets the right glHelper + m_ownCurrent = !(m_gl->surface() == m_surface); + if (m_ownCurrent && !makeCurrent(m_surface)) + return false; + + // TODO: cache surface format somewhere rather than doing this every time render surface changes + resolveRenderTargetFormat(); + + // Sets or Create the correct m_glHelper + // for the current surface + activateGLHelper(); + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + GLint err = m_gl->functions()->glGetError(); + if (err != 0) { + qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err; + } +#endif + + if (!m_initialized) { + initialize(); + } + + // need to reset these values every frame, may get overwritten elsewhere + m_gl->functions()->glClearColor(m_currClearColorValue.redF(), m_currClearColorValue.greenF(), m_currClearColorValue.blueF(), m_currClearColorValue.alphaF()); + m_gl->functions()->glClearDepthf(m_currClearDepthValue); + m_gl->functions()->glClearStencil(m_currClearStencilValue); + + + if (m_activeShader) { + m_activeShader = nullptr; + m_activeShaderDNA = 0; + } + + // reset active textures + for (int u = 0; u < m_activeTextures.size(); ++u) + m_activeTextures[u].texture = nullptr; + + m_boundArrayBuffer = nullptr; + + static int callCount = 0; + ++callCount; + const int shaderPurgePeriod = 600; + if (callCount % shaderPurgePeriod == 0) + m_shaderCache->purge(); + + return true; +} + +void SubmissionContext::endDrawing(bool swapBuffers) +{ + if (swapBuffers) + m_gl->swapBuffers(m_surface); + if (m_ownCurrent) + m_gl->doneCurrent(); + decayTextureScores(); +} + +void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, GLuint defaultFboId) +{ + GLuint fboId = defaultFboId; // Default FBO + if (renderTargetNodeId) { + // New RenderTarget + if (!m_renderTargets.contains(renderTargetNodeId)) { + if (m_defaultFBO && fboId == m_defaultFBO) { + // this is the default fbo that some platforms create (iOS), we just register it + // Insert FBO into hash + m_renderTargets.insert(renderTargetNodeId, fboId); + } else { + fboId = createRenderTarget(renderTargetNodeId, attachments); + } + } else { + fboId = updateRenderTarget(renderTargetNodeId, attachments, true); + } + } + m_activeFBO = fboId; + m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw); + // Set active drawBuffers + activateDrawBuffers(attachments); +} + +GLuint SubmissionContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments) +{ + const GLuint fboId = m_glHelper->createFrameBufferObject(); + if (fboId) { + // The FBO is created and its attachments are set once + // Insert FBO into hash + m_renderTargets.insert(renderTargetNodeId, fboId); + // Bind FBO + m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); + bindFrameBufferAttachmentHelper(fboId, attachments); + } else { + qCritical("Failed to create FBO"); + } + return fboId; +} + +GLuint SubmissionContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget) +{ + const GLuint fboId = m_renderTargets.value(renderTargetNodeId); + + // We need to check if one of the attachment was resized + bool needsResize = !m_renderTargetsSize.contains(fboId); // not even initialized yet? + if (!needsResize) { + // render target exists, has attachment been resized? + GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager(); + const QSize s = m_renderTargetsSize[fboId]; + const auto attachments_ = attachments.attachments(); + for (const Attachment &attachment : attachments_) { + GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid); + // ### TODO QTBUG-64757 this check is insufficient since the + // texture may have changed to another one with the same size. That + // case is not handled atm. + needsResize |= (rTex != nullptr && rTex->size() != s); + if (isActiveRenderTarget) { + if (attachment.m_point == QRenderTargetOutput::Color0) + m_renderTargetFormat = rTex->properties().format; + } + } + } + + if (needsResize) { + m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); + bindFrameBufferAttachmentHelper(fboId, attachments); + } + + return fboId; +} + +QSize SubmissionContext::renderTargetSize(const QSize &surfaceSize) const +{ + QSize renderTargetSize; + if (m_activeFBO != m_defaultFBO) { + // For external FBOs we may not have a m_renderTargets entry. + if (m_renderTargetsSize.contains(m_activeFBO)) { + renderTargetSize = m_renderTargetsSize[m_activeFBO]; + } else if (surfaceSize.isValid()) { + renderTargetSize = surfaceSize; + } else { + // External FBO (when used with QtQuick2 Scene3D) + + // Query FBO color attachment 0 size + GLint attachmentObjectType = GL_NONE; + GLint attachment0Name = 0; + m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + &attachmentObjectType); + m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + &attachment0Name); + + if (attachmentObjectType == GL_RENDERBUFFER && m_glHelper->supportsFeature(GraphicsHelperInterface::RenderBufferDimensionRetrieval)) + renderTargetSize = m_glHelper->getRenderBufferDimensions(attachment0Name); + else if (attachmentObjectType == GL_TEXTURE && m_glHelper->supportsFeature(GraphicsHelperInterface::TextureDimensionRetrieval)) + // Assumes texture level 0 and GL_TEXTURE_2D target + renderTargetSize = m_glHelper->getTextureDimensions(attachment0Name, GL_TEXTURE_2D); + else + return renderTargetSize; + } + } else { + renderTargetSize = m_surface->size(); + if (m_surface->surfaceClass() == QSurface::Window) { + int dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio(); + renderTargetSize *= dpr; + } + } + return renderTargetSize; +} + +QImage SubmissionContext::readFramebuffer(const QRect &rect) +{ + QImage img; + const unsigned int area = rect.width() * rect.height(); + unsigned int bytes; + GLenum format, type; + QImage::Format imageFormat; + uint stride; + + /* format value should match GL internalFormat */ + GLenum internalFormat = m_renderTargetFormat; + + switch (m_renderTargetFormat) { + case QAbstractTexture::RGBAFormat: + case QAbstractTexture::RGBA8_SNorm: + case QAbstractTexture::RGBA8_UNorm: + case QAbstractTexture::RGBA8U: + case QAbstractTexture::SRGB8_Alpha8: +#ifdef QT_OPENGL_ES_2 + format = GL_RGBA; + imageFormat = QImage::Format_RGBA8888_Premultiplied; +#else + format = GL_BGRA; + imageFormat = QImage::Format_ARGB32_Premultiplied; + internalFormat = GL_RGBA8; +#endif + type = GL_UNSIGNED_BYTE; + bytes = area * 4; + stride = rect.width() * 4; + break; + case QAbstractTexture::SRGB8: + case QAbstractTexture::RGBFormat: + case QAbstractTexture::RGB8U: + case QAbstractTexture::RGB8_UNorm: +#ifdef QT_OPENGL_ES_2 + format = GL_RGBA; + imageFormat = QImage::Format_RGBX8888; +#else + format = GL_BGRA; + imageFormat = QImage::Format_RGB32; + internalFormat = GL_RGB8; +#endif + type = GL_UNSIGNED_BYTE; + bytes = area * 4; + stride = rect.width() * 4; + break; +#ifndef QT_OPENGL_ES_2 + case QAbstractTexture::RG11B10F: + bytes = area * 4; + format = GL_RGB; + type = GL_UNSIGNED_INT_10F_11F_11F_REV; + imageFormat = QImage::Format_RGB30; + stride = rect.width() * 4; + break; + case QAbstractTexture::RGB10A2: + bytes = area * 4; + format = GL_RGBA; + type = GL_UNSIGNED_INT_2_10_10_10_REV; + imageFormat = QImage::Format_A2BGR30_Premultiplied; + stride = rect.width() * 4; + break; + case QAbstractTexture::R5G6B5: + bytes = area * 2; + format = GL_RGB; + type = GL_UNSIGNED_SHORT; + internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV; + imageFormat = QImage::Format_RGB16; + stride = rect.width() * 2; + break; + case QAbstractTexture::RGBA16F: + case QAbstractTexture::RGBA16U: + case QAbstractTexture::RGBA32F: + case QAbstractTexture::RGBA32U: + bytes = area * 16; + format = GL_RGBA; + type = GL_FLOAT; + imageFormat = QImage::Format_ARGB32_Premultiplied; + stride = rect.width() * 16; + break; +#endif + default: + // unsupported format + Q_UNREACHABLE(); + return img; + } + + GLint samples = 0; + m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples); + if (samples > 0 && !m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) { + qWarning () << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; " + "Required feature BlitFramebuffer is missing."; + return img; + } + + img = QImage(rect.width(), rect.height(), imageFormat); + + QScopedArrayPointer<uchar> data(new uchar [bytes]); + + if (samples > 0) { + // resolve multisample-framebuffer to renderbuffer and read pixels from it + GLuint fbo, rb; + QOpenGLFunctions *gl = m_gl->functions(); + gl->glGenFramebuffers(1, &fbo); + gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + gl->glGenRenderbuffers(1, &rb); + gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); + gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(), rect.height()); + gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); + + const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + gl->glDeleteRenderbuffers(1, &rb); + gl->glDeleteFramebuffers(1, &fbo); + qWarning () << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status; + return img; + } + + m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(), + 0, 0, rect.width(), rect.height(), + GL_COLOR_BUFFER_BIT, GL_NEAREST); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data()); + + copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); + + gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); + gl->glDeleteRenderbuffers(1, &rb); + gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO); + gl->glDeleteFramebuffers(1, &fbo); + } else { + // read pixels directly from framebuffer + m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), format, type, data.data()); + copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); + } + + return img; +} + +void SubmissionContext::setViewport(const QRectF &viewport, const QSize &surfaceSize) +{ + // // save for later use; this has nothing to do with the viewport but it is + // // here that we get to know the surfaceSize from the RenderView. + m_surfaceSize = surfaceSize; + + m_viewport = viewport; + QSize size = renderTargetSize(surfaceSize); + + // Check that the returned size is before calling glViewport + if (size.isEmpty()) + return; + + // Qt3D 0------------------> 1 OpenGL 1^ + // | | + // | | + // | | + // V | + // 1 0---------------------> 1 + // The Viewport is defined between 0 and 1 which allows us to automatically + // scale to the size of the provided window surface + m_gl->functions()->glViewport(m_viewport.x() * size.width(), + (1.0 - m_viewport.y() - m_viewport.height()) * size.height(), + m_viewport.width() * size.width(), + m_viewport.height() * size.height()); +} + +void SubmissionContext::releaseOpenGL() +{ + m_shaderCache->clear(); + m_renderBufferHash.clear(); + + // Stop and destroy the OpenGL logger + if (m_debugLogger) { + m_debugLogger->stopLogging(); + m_debugLogger.reset(nullptr); + } +} + +// The OpenGLContext is not current on any surface at this point +void SubmissionContext::setOpenGLContext(QOpenGLContext* ctx) +{ + Q_ASSERT(ctx && m_shaderCache); + + releaseOpenGL(); + m_gl = ctx; +} + +void SubmissionContext::activateGLHelper() +{ + // Sets the correct GL Helper depending on the surface + // If no helper exists, create one + m_glHelper = m_glHelpers.value(m_surface); + if (!m_glHelper) { + m_glHelper = resolveHighestOpenGLFunctions(); + m_glHelpers.insert(m_surface, m_glHelper); + // Note: OpenGLContext is current at this point + m_gl->functions()->glDisable(GL_DITHER); + } +} + + +// Called only from RenderThread +bool SubmissionContext::activateShader(ProgramDNA shaderDNA) +{ + if (shaderDNA != m_activeShaderDNA) { + // Ensure material uniforms are re-applied + m_material = nullptr; + + m_activeShader = m_shaderCache->getShaderProgramForDNA(shaderDNA); + if (Q_LIKELY(m_activeShader != nullptr)) { + m_activeShader->bind(); + m_activeShaderDNA = shaderDNA; + } else { + m_glHelper->useProgram(0); + qWarning() << "No shader program found for DNA"; + m_activeShaderDNA = 0; + return false; + } + } + return true; +} + +void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments) +{ + // Set FBO attachments. These are normally textures, except that on Open GL + // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is + // desired since this cannot be achieved neither with a single texture (not + // before GLES 3.2) nor with separate textures (no suitable format for + // stencil before 3.1 with the appropriate extension). + + QSize fboSize; + GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager(); + const auto attachments_ = attachments.attachments(); + for (const Attachment &attachment : attachments_) { + GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid); + if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) { + QOpenGLTexture *glTex = rTex ? rTex->getOrCreateGLTexture() : nullptr; + if (glTex != nullptr) { + if (fboSize.isEmpty()) + fboSize = QSize(glTex->width(), glTex->height()); + else + fboSize = QSize(qMin(fboSize.width(), glTex->width()), qMin(fboSize.height(), glTex->height())); + m_glHelper->bindFrameBufferAttachment(glTex, attachment); + } + } else { + RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr; + if (renderBuffer) { + if (fboSize.isEmpty()) + fboSize = QSize(renderBuffer->width(), renderBuffer->height()); + else + fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()), qMin(fboSize.height(), renderBuffer->height())); + m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment); + } + } + } + m_renderTargetsSize.insert(fboId, fboSize); +} + +void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments) +{ + const QVector<int> activeDrawBuffers = attachments.getGlDrawBuffers(); + + if (m_glHelper->checkFrameBufferComplete()) { + if (activeDrawBuffers.size() > 1) {// We need MRT + if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) { + // Set up MRT, glDrawBuffers... + m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data()); + } + } + } else { + qWarning() << "FBO incomplete"; + } +} + + +void SubmissionContext::setActiveMaterial(Material *rmat) +{ + if (m_material == rmat) + return; + + deactivateTexturesWithScope(TextureScopeMaterial); + m_material = rmat; +} + +int SubmissionContext::activateTexture(TextureScope scope, GLTexture *tex, int onUnit) +{ + // Returns the texture unit to use for the texture + // This always return a valid unit, unless there are more textures than + // texture unit available for the current material + onUnit = assignUnitForTexture(tex); + + // check we didn't overflow the available units + if (onUnit == -1) + return -1; + + // actually re-bind if required, the tex->dna on the unit not being the same + // Note: tex->dna() could be 0 if the texture has not been created yet + if (m_activeTextures[onUnit].texture != tex) { + QOpenGLTexture *glTex = tex->getOrCreateGLTexture(); + if (glTex == nullptr) + return -1; + glTex->bind(onUnit); + m_activeTextures[onUnit].texture = tex; + } + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + int err = m_gl->functions()->glGetError(); + if (err) + qCWarning(Backend) << "GL error after activating texture" << QString::number(err, 16) + << tex->textureId() << "on unit" << onUnit; +#endif + + m_activeTextures[onUnit].score = 200; + m_activeTextures[onUnit].pinned = true; + m_activeTextures[onUnit].scope = scope; + + return onUnit; +} + +void SubmissionContext::deactivateTexturesWithScope(TextureScope ts) +{ + for (int u=0; u<m_activeTextures.size(); ++u) { + if (!m_activeTextures[u].pinned) + continue; // inactive, ignore + + if (m_activeTextures[u].scope == ts) { + m_activeTextures[u].pinned = false; + m_activeTextures[u].score = qMax(m_activeTextures[u].score, 1) - 1; + } + } // of units iteration +} + +void SubmissionContext::deactivateTexture(GLTexture* tex) +{ + for (int u=0; u<m_activeTextures.size(); ++u) { + if (m_activeTextures[u].texture == tex) { + Q_ASSERT(m_activeTextures[u].pinned); + m_activeTextures[u].pinned = false; + return; + } + } // of units iteration + + qCWarning(Backend) << Q_FUNC_INFO << "texture not active:" << tex; +} + +/*! + \internal + Returns a texture unit for a texture, -1 if all texture units are assigned. + Tries to use the texture unit with the texture that hasn't been used for the longest time + if the texture happens not to be already pinned on a texture unit. + */ +GLint SubmissionContext::assignUnitForTexture(GLTexture *tex) +{ + int lowestScoredUnit = -1; + int lowestScore = 0xfffffff; + + for (int u=0; u<m_activeTextures.size(); ++u) { + if (m_activeTextures[u].texture == tex) + return u; + + // No texture is currently active on the texture unit + // we save the texture unit with the texture that has been on there + // the longest time while not being used + if (!m_activeTextures[u].pinned) { + int score = m_activeTextures[u].score; + if (score < lowestScore) { + lowestScore = score; + lowestScoredUnit = u; + } + } + } // of units iteration + + if (lowestScoredUnit == -1) + qCWarning(Backend) << Q_FUNC_INFO << "No free texture units!"; + + return lowestScoredUnit; +} + +void SubmissionContext::decayTextureScores() +{ + for (int u = 0; u < m_activeTextures.size(); u++) + m_activeTextures[u].score = qMax(m_activeTextures[u].score - 1, 0); +} + +void SubmissionContext::setCurrentStateSet(RenderStateSet *ss) +{ + if (ss == m_stateSet) + return; + if (ss) + ss->apply(this); + m_stateSet = ss; +} + +RenderStateSet *SubmissionContext::currentStateSet() const +{ + return m_stateSet; +} + +void SubmissionContext::clearColor(const QColor &color) +{ + if (m_currClearColorValue != color) { + m_currClearColorValue = color; + m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); + } +} + +void SubmissionContext::clearDepthValue(float depth) +{ + if (m_currClearDepthValue != depth) { + m_currClearDepthValue = depth; + m_gl->functions()->glClearDepthf(depth); + } +} + +void SubmissionContext::clearStencilValue(int stencil) +{ + if (m_currClearStencilValue != stencil) { + m_currClearStencilValue = stencil; + m_gl->functions()->glClearStencil(stencil); + } +} + +// It will be easier if the QGraphicContext applies the QUniformPack +// than the other way around +bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) +{ + // Activate textures and update TextureUniform in the pack + // with the correct textureUnit + + // Set the pinned texture of the previous material texture + // to pinable so that we should easily find an available texture unit + NodeManagers *manager = m_renderer->nodeManagers(); + + deactivateTexturesWithScope(TextureScopeMaterial); + // Update the uniforms with the correct texture unit id's + PackUniformHash &uniformValues = parameterPack.uniforms(); + + for (int i = 0; i < parameterPack.textures().size(); ++i) { + const ShaderParameterPack::NamedTexture &namedTex = parameterPack.textures().at(i); + // Given a Texture QNodeId, we retrieve the associated shared GLTexture + if (uniformValues.contains(namedTex.glslNameId)) { + GLTexture *t = manager->glTextureManager()->lookupResource(namedTex.texId); + if (t != nullptr) { + UniformValue &texUniform = uniformValues[namedTex.glslNameId]; + Q_ASSERT(texUniform.valueType() == UniformValue::TextureValue); + const int texUnit = activateTexture(TextureScopeMaterial, t); + texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit; + if (texUnit == -1) + return false; + } + } + } + + QOpenGLShaderProgram *shader = activeShader(); + + // TO DO: We could cache the binding points somehow and only do the binding when necessary + // for SSBO and UBO + + // Bind Shader Storage block to SSBO and update SSBO + const QVector<BlockToSSBO> blockToSSBOs = parameterPack.shaderStorageBuffers(); + int ssboIndex = 0; + for (const BlockToSSBO b : blockToSSBOs) { + Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); + GLBuffer *ssbo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::ShaderStorageBuffer); + bindShaderStorageBlock(shader->programId(), b.m_blockIndex, ssboIndex); + // Needed to avoid conflict where the buffer would already + // be bound as a VertexArray + bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer); + ssbo->bindBufferBase(this, ssboIndex++, GLBuffer::ShaderStorageBuffer); + // TO DO: Make sure that there's enough binding points + } + + // Bind UniformBlocks to UBO and update UBO from Buffer + // TO DO: Convert ShaderData to Buffer so that we can use that generic process + const QVector<BlockToUBO> blockToUBOs = parameterPack.uniformBuffers(); + int uboIndex = 0; + for (const BlockToUBO &b : blockToUBOs) { + Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); + GLBuffer *ubo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::UniformBuffer); + bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex); + // Needed to avoid conflict where the buffer would already + // be bound as a VertexArray + bindGLBuffer(ubo, GLBuffer::UniformBuffer); + ubo->bindBufferBase(this, uboIndex++, GLBuffer::UniformBuffer); + // TO DO: Make sure that there's enough binding points + } + + // Update uniforms in the Default Uniform Block + const PackUniformHash values = parameterPack.uniforms(); + const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms(); + + for (const ShaderUniform &uniform : activeUniforms) { + // We can use [] as we are sure the the uniform wouldn't + // be un activeUniforms if there wasn't a matching value + const UniformValue &v = values[uniform.m_nameId]; + + // skip invalid textures + if (v.valueType() == UniformValue::TextureValue && *v.constData<int>() == -1) + continue; + + applyUniform(uniform, v); + } + // if not all data is valid, the next frame will be rendered immediately + return true; +} + +void SubmissionContext::enableAttribute(const VAOVertexAttribute &attr) +{ + // Bind buffer within the current VAO + GLBuffer *buf = m_renderer->nodeManagers()->glBufferManager()->data(attr.bufferHandle); + Q_ASSERT(buf); + bindGLBuffer(buf, attr.attributeType); + + // Don't use QOpenGLShaderProgram::setAttributeBuffer() because of QTBUG-43199. + // Use the introspection data and set the attribute explicitly + m_glHelper->enableVertexAttributeArray(attr.location); + m_glHelper->vertexAttributePointer(attr.shaderDataType, + attr.location, + attr.vertexSize, + attr.dataType, + GL_TRUE, // TODO: Support normalization property on QAttribute + attr.byteStride, + reinterpret_cast<const void *>(qintptr(attr.byteOffset))); + + + // Done by the helper if it supports it + if (attr.divisor != 0) + m_glHelper->vertexAttribDivisor(attr.location, attr.divisor); +} + +void SubmissionContext::disableAttribute(const SubmissionContext::VAOVertexAttribute &attr) +{ + QOpenGLShaderProgram *prog = activeShader(); + prog->disableAttributeArray(attr.location); +} + +// Note: needs to be called while VAO is bound +void SubmissionContext::specifyAttribute(const Attribute *attribute, + Buffer *buffer, + const ShaderAttribute *attributeDescription) +{ + const int location = attributeDescription->m_location; + if (location < 0) { + qCWarning(Backend) << "failed to resolve location for attribute:" << attribute->name(); + return; + } + + const GLint attributeDataType = glDataTypeFromAttributeDataType(attribute->vertexBaseType()); + const HGLBuffer glBufferHandle = m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()); + Q_ASSERT(!glBufferHandle.isNull()); + const GLBuffer::Type attributeType = attributeTypeToGLBufferType(attribute->attributeType()); + + int typeSize = 0; + int attrCount = 0; + + if (attribute->vertexSize() >= 1 && attribute->vertexSize() <= 4) { + attrCount = 1; + } else if (attribute->vertexSize() == 9) { + typeSize = byteSizeFromType(attributeDataType); + attrCount = 3; + } else if (attribute->vertexSize() == 16) { + typeSize = byteSizeFromType(attributeDataType); + attrCount = 4; + } else { + Q_UNREACHABLE(); + } + + for (int i = 0; i < attrCount; i++) { + VAOVertexAttribute attr; + attr.bufferHandle = glBufferHandle; + attr.attributeType = attributeType; + attr.location = location + i; + attr.dataType = attributeDataType; + attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize); + attr.vertexSize = attribute->vertexSize() / attrCount; + attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize); + attr.divisor = attribute->divisor(); + attr.shaderDataType = attributeDescription->m_type; + + enableAttribute(attr); + + // Save this in the current emulated VAO + if (m_currentVAO) + m_currentVAO->saveVertexAttribute(attr); + } +} + +void SubmissionContext::specifyIndices(Buffer *buffer) +{ + GLBuffer *buf = glBufferForRenderBuffer(buffer, GLBuffer::IndexBuffer); + if (!bindGLBuffer(buf, GLBuffer::IndexBuffer)) + qCWarning(Backend) << Q_FUNC_INFO << "binding index buffer failed"; + + // bound within the current VAO + // Save this in the current emulated VAO + if (m_currentVAO) + m_currentVAO->saveIndexAttribute(m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId())); +} + +void SubmissionContext::updateBuffer(Buffer *buffer) +{ + const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); + if (it != m_renderBufferHash.end()) + uploadDataToGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value())); +} + +QByteArray SubmissionContext::downloadBufferContent(Buffer *buffer) +{ + const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); + if (it != m_renderBufferHash.end()) + return downloadDataFromGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value())); + return QByteArray(); +} + +void SubmissionContext::releaseBuffer(Qt3DCore::QNodeId bufferId) +{ + auto it = m_renderBufferHash.find(bufferId); + if (it != m_renderBufferHash.end()) { + HGLBuffer glBuffHandle = it.value(); + GLBuffer *glBuff = m_renderer->nodeManagers()->glBufferManager()->data(glBuffHandle); + + Q_ASSERT(glBuff); + // Destroy the GPU resource + glBuff->destroy(this); + // Destroy the GLBuffer instance + m_renderer->nodeManagers()->glBufferManager()->releaseResource(bufferId); + // Remove Id - HGLBuffer entry + m_renderBufferHash.erase(it); + } +} + +bool SubmissionContext::hasGLBufferForBuffer(Buffer *buffer) +{ + const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); + return (it != m_renderBufferHash.end()); +} + +GLBuffer *SubmissionContext::glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type) +{ + if (!m_renderBufferHash.contains(buf->peerId())) + m_renderBufferHash.insert(buf->peerId(), createGLBufferFor(buf, type)); + return m_renderer->nodeManagers()->glBufferManager()->data(m_renderBufferHash.value(buf->peerId())); +} + +HGLBuffer SubmissionContext::createGLBufferFor(Buffer *buffer, GLBuffer::Type type) +{ + GLBuffer *b = m_renderer->nodeManagers()->glBufferManager()->getOrCreateResource(buffer->peerId()); + // b.setUsagePattern(static_cast<QOpenGLBuffer::UsagePattern>(buffer->usage())); + Q_ASSERT(b); + if (!b->create(this)) + qCWarning(Render::Io) << Q_FUNC_INFO << "buffer creation failed"; + + if (!bindGLBuffer(b, type)) + qCWarning(Render::Io) << Q_FUNC_INFO << "buffer binding failed"; + + return m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()); +} + +bool SubmissionContext::bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type) +{ + if (type == GLBuffer::ArrayBuffer && buffer == m_boundArrayBuffer) + return true; + + if (buffer->bind(this, type)) { + if (type == GLBuffer::ArrayBuffer) + m_boundArrayBuffer = buffer; + return true; + } + return false; +} + +void SubmissionContext::uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer) +{ + if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're uploading, the type doesn't matter here + qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed"; + // If the buffer is dirty (hence being called here) + // there are two possible cases + // * setData was called changing the whole data or functor (or the usage pattern) + // * partial buffer updates where received + + // TO DO: Handle usage pattern + QVector<Qt3DRender::QBufferUpdate> updates = std::move(buffer->pendingBufferUpdates()); + for (auto it = updates.begin(); it != updates.end(); ++it) { + auto update = it; + // We have a partial update + if (update->offset >= 0) { + //accumulate sequential updates as single one + int bufferSize = update->data.size(); + auto it2 = it + 1; + while ((it2 != updates.end()) + && (it2->offset - update->offset == bufferSize)) { + bufferSize += it2->data.size(); + ++it2; + } + update->data.resize(bufferSize); + while (it + 1 != it2) { + ++it; + update->data.replace(it->offset - update->offset, it->data.size(), it->data); + it->data.clear(); + } + // TO DO: based on the number of updates .., it might make sense to + // sometime use glMapBuffer rather than glBufferSubData + b->update(this, update->data.constData(), update->data.size(), update->offset); + } else { + // We have an update that was done by calling QBuffer::setData + // which is used to resize or entirely clear the buffer + // Note: we use the buffer data directly in that case + const int bufferSize = buffer->data().size(); + b->allocate(this, bufferSize, false); // orphan the buffer + b->allocate(this, buffer->data().constData(), bufferSize, false); + } + } + + if (releaseBuffer) { + b->release(this); + m_boundArrayBuffer = nullptr; + } + qCDebug(Render::Io) << "uploaded buffer size=" << buffer->data().size(); +} + +QByteArray SubmissionContext::downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b) +{ + if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here + qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed"; + + QByteArray data = b->download(this, buffer->data().size()); + return data; +} + +void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId, + Qt3DCore::QNodeId outputRenderTargetId, + QRect inputRect, QRect outputRect, + uint defaultFboId, + QRenderTargetOutput::AttachmentPoint inputAttachmentPoint, + QRenderTargetOutput::AttachmentPoint outputAttachmentPoint, + QBlitFramebuffer::InterpolationMethod interpolationMethod) +{ + GLuint inputFboId = defaultFboId; + bool inputBufferIsDefault = true; + if (!inputRenderTargetId.isNull()) { + RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(inputRenderTargetId); + if (renderTarget) { + AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager()); + if (m_renderTargets.contains(inputRenderTargetId)) + inputFboId = updateRenderTarget(inputRenderTargetId, attachments, false); + else + inputFboId = createRenderTarget(inputRenderTargetId, attachments); + } + inputBufferIsDefault = false; + } + + GLuint outputFboId = defaultFboId; + bool outputBufferIsDefault = true; + if (!outputRenderTargetId.isNull()) { + RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(outputRenderTargetId); + if (renderTarget) { + AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager()); + if (m_renderTargets.contains(outputRenderTargetId)) + outputFboId = updateRenderTarget(outputRenderTargetId, attachments, false); + else + outputFboId = createRenderTarget(outputRenderTargetId, attachments); + } + outputBufferIsDefault = false; + } + + // Up until this point the input and output rects are normal Qt rectangles. + // Convert them to GL rectangles (Y at bottom). + const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[inputFboId].height(); + const GLint srcX0 = inputRect.left(); + const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height()); + const GLint srcX1 = srcX0 + inputRect.width(); + const GLint srcY1 = srcY0 + inputRect.height(); + + const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[outputFboId].height(); + const GLint dstX0 = outputRect.left(); + const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height()); + const GLint dstX1 = dstX0 + outputRect.width(); + const GLint dstY1 = dstY0 + outputRect.height(); + + //Get the last bounded framebuffers + const GLuint lastDrawFboId = boundFrameBufferObject(); + + // Activate input framebuffer for reading + bindFramebuffer(inputFboId, GraphicsHelperInterface::FBORead); + + // Activate output framebuffer for writing + bindFramebuffer(outputFboId, GraphicsHelperInterface::FBODraw); + + //Bind texture + if (!inputBufferIsDefault) + readBuffer(GL_COLOR_ATTACHMENT0 + inputAttachmentPoint); + + if (!outputBufferIsDefault) + drawBuffer(GL_COLOR_ATTACHMENT0 + outputAttachmentPoint); + + // Blit framebuffer + const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR; + m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, + GL_COLOR_BUFFER_BIT, mode); + + // Reset draw buffer + bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw); +} + +} // namespace Render +} // namespace Qt3DRender of namespace + +QT_END_NAMESPACE diff --git a/src/render/graphicshelpers/submissioncontext_p.h b/src/render/graphicshelpers/submissioncontext_p.h new file mode 100644 index 000000000..8efdcbc63 --- /dev/null +++ b/src/render/graphicshelpers/submissioncontext_p.h @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_SUBMISSIONCONTEXT_H +#define QT3DRENDER_RENDER_SUBMISSIONCONTEXT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/qclearbuffers.h> +#include <Qt3DRender/private/glbuffer_p.h> +#include <Qt3DRender/qattribute.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/shadercache_p.h> + +QT_BEGIN_NAMESPACE + +class QAbstractOpenGLFunctions; + +namespace Qt3DRender { + +namespace Render { + +class Renderer; +class GraphicsHelperInterface; +class RenderStateSet; +class Material; +class GLTexture; +class RenderCommand; +class RenderTarget; +class AttachmentPack; +class Attribute; +class Buffer; +class ShaderManager; + +enum TextureScope +{ + TextureScopeMaterial = 0, + TextureScopeTechnique + // per-pass for deferred rendering? +}; + +typedef QPair<QString, int> NamedUniformLocation; + +class Q_AUTOTEST_EXPORT SubmissionContext : public GraphicsContext +{ +public: + SubmissionContext(); + ~SubmissionContext(); + + int id() const; // unique, small integer ID of this context + void setRenderer(Renderer *renderer) { m_renderer = renderer; } + + bool beginDrawing(QSurface *surface); + void endDrawing(bool swapBuffers); + void activateGLHelper(); + void releaseOpenGL(); + void setOpenGLContext(QOpenGLContext* ctx); + + // Viewport + void setViewport(const QRectF &viewport, const QSize &surfaceSize); + QRectF viewport() const { return m_viewport; } + + // Shaders + bool activateShader(ProgramDNA shaderDNA); + QOpenGLShaderProgram *activeShader() const { return m_activeShader; } + + // FBO + GLuint activeFBO() const { return m_activeFBO; } + void activateRenderTarget(const Qt3DCore::QNodeId id, const AttachmentPack &attachments, GLuint defaultFboId); + QSize renderTargetSize(const QSize &surfaceSize) const; + QImage readFramebuffer(const QRect &rect); + void blitFramebuffer(Qt3DCore::QNodeId outputRenderTargetId, Qt3DCore::QNodeId inputRenderTargetId, + QRect inputRect, + QRect outputRect, uint defaultFboId, + QRenderTargetOutput::AttachmentPoint inputAttachmentPoint, + QRenderTargetOutput::AttachmentPoint outputAttachmentPoint, + QBlitFramebuffer::InterpolationMethod interpolationMethod); + + + // Material + Material* activeMaterial() const { return m_material; } + void setActiveMaterial(Material* rmat); + + // Attributes + void specifyAttribute(const Attribute *attribute, + Buffer *buffer, + const ShaderAttribute *attributeDescription); + void specifyIndices(Buffer *buffer); + + // Buffer + void updateBuffer(Buffer *buffer); + QByteArray downloadBufferContent(Buffer *buffer); + void releaseBuffer(Qt3DCore::QNodeId bufferId); + bool hasGLBufferForBuffer(Buffer *buffer); + GLBuffer *glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type); + + // Parameters + bool setParameters(ShaderParameterPack ¶meterPack); + + // Textures + int activateTexture(TextureScope scope, GLTexture* tex, int onUnit = -1); + void deactivateTexture(GLTexture *tex); + + // RenderState + void setCurrentStateSet(RenderStateSet* ss); + RenderStateSet *currentStateSet() const; + + // Wrappers + void clearColor(const QColor &color); + void clearDepthValue(float depth); + void clearStencilValue(int stencil); + +private: + void initialize(); + + // Textures + void decayTextureScores(); + GLint assignUnitForTexture(GLTexture* tex); + void deactivateTexturesWithScope(TextureScope ts); + + // FBO + void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments); + void activateDrawBuffers(const AttachmentPack &attachments); + void resolveRenderTargetFormat(); + GLuint createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments); + GLuint updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget); + + + // Buffers + HGLBuffer createGLBufferFor(Buffer *buffer, GLBuffer::Type type); + void uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer = false); + QByteArray downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b); + bool bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type); + + bool m_initialized; + bool m_ownCurrent; + const unsigned int m_id; + QSurface *m_surface; + QSize m_surfaceSize; + + QOpenGLShaderProgram *m_activeShader; + ProgramDNA m_activeShaderDNA; + + QHash<Qt3DCore::QNodeId, HGLBuffer> m_renderBufferHash; + QHash<Qt3DCore::QNodeId, GLuint> m_renderTargets; + QHash<GLuint, QSize> m_renderTargetsSize; + QAbstractTexture::TextureFormat m_renderTargetFormat; + + QHash<QSurface *, GraphicsHelperInterface*> m_glHelpers; + + // active textures, indexed by texture unit + struct ActiveTexture { + GLTexture *texture = nullptr; + int score = 0; + TextureScope scope = TextureScopeMaterial; + bool pinned = false; + }; + QVector<ActiveTexture> m_activeTextures; + + // cache some current state, to make sure we don't issue unnecessary GL calls + int m_currClearStencilValue; + float m_currClearDepthValue; + QColor m_currClearColorValue; + + Material* m_material; + QRectF m_viewport; + GLuint m_activeFBO; + + GLBuffer *m_boundArrayBuffer; + RenderStateSet* m_stateSet; + Renderer *m_renderer; + QByteArray m_uboTempArray; + + + // Attributes + friend class OpenGLVertexArrayObject; + OpenGLVertexArrayObject *m_currentVAO; + + struct VAOVertexAttribute + { + HGLBuffer bufferHandle; + GLBuffer::Type attributeType; + int location; + GLint dataType; + uint byteOffset; + uint vertexSize; + uint byteStride; + uint divisor; + GLenum shaderDataType; + }; + + using VAOIndexAttribute = HGLBuffer; + void enableAttribute(const VAOVertexAttribute &attr); + void disableAttribute(const VAOVertexAttribute &attr); +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_SUBMISSIONCONTEXT_H diff --git a/src/render/jobs/filtercompatibletechniquejob.cpp b/src/render/jobs/filtercompatibletechniquejob.cpp index 080ccd306..342fd3dad 100644 --- a/src/render/jobs/filtercompatibletechniquejob.cpp +++ b/src/render/jobs/filtercompatibletechniquejob.cpp @@ -42,7 +42,7 @@ #include <Qt3DRender/private/nodemanagers_p.h> #include <Qt3DRender/private/renderer_p.h> #include <Qt3DRender/private/job_common_p.h> -#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/submissioncontext_p.h> QT_BEGIN_NAMESPACE @@ -79,7 +79,7 @@ Renderer *FilterCompatibleTechniqueJob::renderer() const void FilterCompatibleTechniqueJob::run() { Q_ASSERT(m_manager != nullptr && m_renderer != nullptr); - Q_ASSERT(m_renderer->isRunning() && m_renderer->graphicsContext()->isInitialized()); + Q_ASSERT(m_renderer->isRunning() && m_renderer->submissionContext()->isInitialized()); const QVector<Qt3DCore::QNodeId> dirtyTechniqueIds = m_manager->takeDirtyTechniques(); for (const Qt3DCore::QNodeId techniqueId : dirtyTechniqueIds) { diff --git a/src/render/renderstates/renderstates.cpp b/src/render/renderstates/renderstates.cpp index d5e12aeab..4a2ec69b0 100644 --- a/src/render/renderstates/renderstates.cpp +++ b/src/render/renderstates/renderstates.cpp @@ -317,7 +317,6 @@ void LineWidth::apply(GraphicsContext *gc) const else gc->openGLContext()->functions()->glDisable(GL_LINE_SMOOTH); - gc->activateGLHelper(); gc->openGLContext()->functions()->glLineWidth(std::get<0>(m_values)); } diff --git a/src/render/renderstates/renderstateset.cpp b/src/render/renderstates/renderstateset.cpp index 4166d41b0..bf84b0e1c 100644 --- a/src/render/renderstates/renderstateset.cpp +++ b/src/render/renderstates/renderstateset.cpp @@ -45,7 +45,7 @@ #include <QDebug> #include <QOpenGLContext> -#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/submissioncontext_p.h> #include <Qt3DRender/private/renderstates_p.h> #include <Qt3DRender/private/qrenderstate_p.h> @@ -139,7 +139,7 @@ int RenderStateSet::changeCost(RenderStateSet *previousState) return cost; } -void RenderStateSet::apply(GraphicsContext *gc) +void RenderStateSet::apply(SubmissionContext *gc) { RenderStateSet* previousStates = gc->currentStateSet(); @@ -176,7 +176,7 @@ void RenderStateSet::merge(RenderStateSet *other) m_stateMask |= other->stateMask(); } -void RenderStateSet::resetMasked(StateMaskSet maskOfStatesToReset, GraphicsContext *gc) +void RenderStateSet::resetMasked(StateMaskSet maskOfStatesToReset, SubmissionContext *gc) { // TO DO -> Call gcHelper methods instead of raw GL // QOpenGLFunctions shouldn't be used here directly diff --git a/src/render/renderstates/renderstateset_p.h b/src/render/renderstates/renderstateset_p.h index 307e0ff1d..58d46c7a6 100644 --- a/src/render/renderstates/renderstateset_p.h +++ b/src/render/renderstates/renderstateset_p.h @@ -65,7 +65,7 @@ class QRenderState; namespace Render { -class GraphicsContext; +class SubmissionContext; class RenderState; class RenderStateSet @@ -89,11 +89,11 @@ public: */ int changeCost(RenderStateSet* previousState); - void apply(GraphicsContext* gc); + void apply(SubmissionContext *gc); StateMaskSet stateMask() const; void merge(RenderStateSet *other); - void resetMasked(StateMaskSet maskOfStatesToReset, GraphicsContext* gc); + void resetMasked(StateMaskSet maskOfStatesToReset, SubmissionContext* gc); template<class State, typename ... Args> static StateVariant createState(Args... values) diff --git a/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp b/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp index 4d4a08a34..626f7811e 100644 --- a/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp +++ b/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp @@ -37,7 +37,7 @@ #include <Qt3DRender/private/filtercompatibletechniquejob_p.h> #include <Qt3DRender/private/nodemanagers_p.h> #include <Qt3DRender/private/renderer_p.h> -#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/submissioncontext_p.h> #include <Qt3DRender/private/qrenderaspect_p.h> #include <Qt3DRender/private/techniquemanager_p.h> @@ -93,7 +93,7 @@ public: { renderer()->setOpenGLContext(&m_glContext); d_func()->m_renderer->initialize(); - renderer()->graphicsContext()->beginDrawing(m_window.data()); + renderer()->submissionContext()->beginDrawing(m_window.data()); } Render::Renderer *renderer() const @@ -209,7 +209,7 @@ private Q_SLOTS: // THEN QCOMPARE(testAspect.renderer()->isRunning(), true); - QCOMPARE(testAspect.renderer()->graphicsContext()->isInitialized(), true); + QCOMPARE(testAspect.renderer()->submissionContext()->isInitialized(), true); const QVector<Qt3DRender::Render::HTechnique> handles = testAspect.nodeManagers()->techniqueManager()->activeHandles(); QCOMPARE(handles.size(), 3); diff --git a/tests/auto/render/renderer/tst_renderer.cpp b/tests/auto/render/renderer/tst_renderer.cpp index 27708c1b9..9a435da28 100644 --- a/tests/auto/render/renderer/tst_renderer.cpp +++ b/tests/auto/render/renderer/tst_renderer.cpp @@ -34,6 +34,7 @@ #include <Qt3DRender/private/viewportnode_p.h> #include <Qt3DRender/private/renderview_p.h> #include <Qt3DRender/private/renderviewbuilder_p.h> +#include <Qt3DRender/private/offscreensurfacehelper_p.h> class tst_Renderer : public QObject { @@ -48,6 +49,7 @@ private Q_SLOTS: // GIVEN Qt3DRender::Render::NodeManagers nodeManagers; Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); + Qt3DRender::Render::OffscreenSurfaceHelper offscreenHelper(&renderer); Qt3DRender::Render::RenderSettings settings; // owned by FG manager Qt3DRender::Render::ViewportNode *fgRoot = new Qt3DRender::Render::ViewportNode(); @@ -58,8 +60,12 @@ private Q_SLOTS: renderer.setNodeManagers(&nodeManagers); renderer.setSettings(&settings); + renderer.setOffscreenSurfaceHelper(&offscreenHelper); renderer.initialize(); + // Ensure invoke calls are performed + QCoreApplication::processEvents(); + // NOTE: FilterCompatibleTechniqueJob and ShaderGathererJob cannot run because the context // is not initialized in this test @@ -204,7 +210,8 @@ private Q_SLOTS: renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); - + // Properly shutdown command thread + renderer.shutdown(); } }; |