summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2018-03-02 10:27:07 +0100
committerPaul Lemire <paul.lemire@kdab.com>2018-03-16 11:21:02 +0000
commitc1b495f70c3b4f0c23d44dfcd99aed5882312100 (patch)
treeced8484d36d999395f520f2d7262d832afc73c99
parent02dff327fae71d6efdda091f8ae0a4483e72e849 (diff)
downloadqt3d-c1b495f70c3b4f0c23d44dfcd99aed5882312100.tar.gz
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.cpp3
-rw-r--r--src/render/backend/offscreensurfacehelper_p.h2
-rw-r--r--src/render/backend/openglvertexarrayobject.cpp12
-rw-r--r--src/render/backend/openglvertexarrayobject_p.h14
-rw-r--r--src/render/backend/renderer.cpp167
-rw-r--r--src/render/backend/renderer_p.h8
-rw-r--r--src/render/graphicshelpers/graphicscontext.cpp1119
-rw-r--r--src/render/graphicshelpers/graphicscontext_p.h186
-rw-r--r--src/render/graphicshelpers/graphicshelpers.pri6
-rw-r--r--src/render/graphicshelpers/submissioncontext.cpp1211
-rw-r--r--src/render/graphicshelpers/submissioncontext_p.h250
-rw-r--r--src/render/jobs/filtercompatibletechniquejob.cpp4
-rw-r--r--src/render/renderstates/renderstates.cpp1
-rw-r--r--src/render/renderstates/renderstateset.cpp6
-rw-r--r--src/render/renderstates/renderstateset_p.h6
-rw-r--r--tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp6
-rw-r--r--tests/auto/render/renderer/tst_renderer.cpp9
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 &parameterPack)
-{
- // 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 &parameterPack);
-
- 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 &parameterPack)
+{
+ // 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 &parameterPack);
+
+ // 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();
}
};