| /**************************************************************************** |
| ** |
| ** Copyright (C) 2019 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of Qt Quick 3D. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** 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 General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) 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.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-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qquick3dscenerenderer_p.h" |
| #include "qquick3dsceneenvironment_p.h" |
| #include "qquick3dobject_p_p.h" |
| #include "qquick3dnode_p.h" |
| #include "qquick3dscenemanager_p.h" |
| #include "qquick3dtexture_p.h" |
| #include "qquick3dcamera_p.h" |
| #include "qquick3dpickresult_p.h" |
| #include "qquick3dmodel_p.h" |
| #include "qquick3drenderstats_p.h" |
| |
| #include <private/qopenglvertexarrayobject_p.h> |
| |
| #include <QtQuick3DRender/private/qssgrenderframebuffer_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendererutil_p.h> |
| #include <QtQuick/QQuickWindow> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static bool dumpPerfTiming = false; |
| static int frameCount = 0; |
| static bool dumpRenderTimes = false; |
| |
| namespace { |
| void cleanupOpenGLState() { |
| QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); |
| |
| gl->glBindBuffer(GL_ARRAY_BUFFER, 0); |
| gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| |
| gl->glActiveTexture(GL_TEXTURE0); |
| gl->glBindTexture(GL_TEXTURE_2D, 0); |
| |
| gl->glDisable(GL_DEPTH_TEST); |
| gl->glDisable(GL_STENCIL_TEST); |
| gl->glDisable(GL_SCISSOR_TEST); |
| |
| gl->glUseProgram(0); |
| |
| QOpenGLFramebufferObject::bindDefault(); |
| } |
| |
| } |
| |
| SGFramebufferObjectNode::SGFramebufferObjectNode() |
| : window(nullptr) |
| , renderer(nullptr) |
| , renderPending(true) |
| , invalidatePending(false) |
| , devicePixelRatio(1) |
| { |
| qsgnode_set_description(this, QStringLiteral("fbonode")); |
| setFlag(QSGNode::UsePreprocess, true); |
| } |
| |
| SGFramebufferObjectNode::~SGFramebufferObjectNode() |
| { |
| delete renderer; |
| delete texture(); |
| } |
| |
| void SGFramebufferObjectNode::scheduleRender() |
| { |
| renderPending = true; |
| markDirty(DirtyMaterial); |
| } |
| |
| QSGTexture *SGFramebufferObjectNode::texture() const |
| { |
| return QSGSimpleTextureNode::texture(); |
| } |
| |
| void SGFramebufferObjectNode::preprocess() |
| { |
| render(); |
| } |
| |
| void SGFramebufferObjectNode::render() |
| { |
| if (renderPending) { |
| if (renderer->renderStats()) |
| renderer->renderStats()->startRender(); |
| |
| renderPending = false; |
| GLuint textureId = renderer->render(); |
| |
| cleanupOpenGLState(); |
| |
| #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) |
| if (texture() && (GLuint(texture()->textureId()) != textureId || texture()->textureSize() != renderer->surfaceSize())) { |
| delete texture(); |
| QSGTexture *wrapper = window->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture, &textureId, 0, |
| renderer->surfaceSize(), QQuickWindow::TextureHasAlphaChannel); |
| setTexture(wrapper); |
| } |
| if (!texture()) { |
| QSGTexture *wrapper = window->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture, &textureId, 0, |
| renderer->surfaceSize(), QQuickWindow::TextureHasAlphaChannel); |
| setTexture(wrapper); |
| } |
| #else |
| if (texture() && (GLuint(texture()->textureId()) != textureId || texture()->textureSize() != renderer->surfaceSize())) { |
| delete texture(); |
| setTexture(window->createTextureFromId(textureId, renderer->surfaceSize(), QQuickWindow::TextureHasAlphaChannel)); |
| } |
| if (!texture()) |
| setTexture(window->createTextureFromId(textureId, renderer->surfaceSize(), QQuickWindow::TextureHasAlphaChannel)); |
| #endif |
| |
| markDirty(QSGNode::DirtyMaterial); |
| emit textureChanged(); |
| |
| if (renderer->renderStats()) { |
| if (dumpRenderTimes) |
| QOpenGLContext::currentContext()->functions()->glFinish(); |
| renderer->renderStats()->endRender(dumpRenderTimes); |
| } |
| if (renderer->m_sgContext->renderer()->rendererRequestsFrames()) { |
| scheduleRender(); |
| window->update(); |
| } |
| } |
| } |
| |
| void SGFramebufferObjectNode::handleScreenChange() |
| { |
| if (!qFuzzyCompare(window->effectiveDevicePixelRatio(), devicePixelRatio)) { |
| renderer->invalidateFramebufferObject(); |
| quickFbo->update(); |
| } |
| } |
| |
| |
| QQuick3DSceneRenderer::QQuick3DSceneRenderer(QWindow *window) |
| : m_window(window) |
| , m_multisampleFbo(nullptr) |
| , m_supersampleFbo(nullptr) |
| , m_fbo(nullptr) |
| { |
| QOpenGLContext *openGLContext = QOpenGLContext::currentContext(); |
| |
| // There is only one Render context per window, so check if one exists for this window already |
| auto renderContextInterface = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); |
| if (!renderContextInterface.isNull()) { |
| m_sgContext = renderContextInterface; |
| m_renderContext = renderContextInterface->renderContext(); |
| } |
| |
| // If there was no render context, then set it up for this window |
| if (m_renderContext.isNull()) |
| m_renderContext = QSSGRenderContext::createGl(openGLContext->format()); |
| if (m_sgContext.isNull()) |
| m_sgContext = QSSGRenderContextInterface::getRenderContextInterface(m_renderContext, QString::fromLatin1("./"), quintptr(window)); |
| |
| |
| dumpPerfTiming = (qEnvironmentVariableIntValue("QUICK3D_PERFTIMERS") > 0); |
| dumpRenderTimes = (qEnvironmentVariableIntValue("QUICK3D_RENDERTIMES") > 0); |
| if (dumpPerfTiming) { |
| m_sgContext->renderer()->enableLayerGpuProfiling(true); |
| m_sgContext->performanceTimer()->setEnabled(true); |
| } |
| } |
| |
| QQuick3DSceneRenderer::~QQuick3DSceneRenderer() |
| { |
| delete m_layer; |
| delete m_fbo; |
| delete m_multisampleFbo; |
| delete m_supersampleFbo; |
| } |
| |
| GLuint QQuick3DSceneRenderer::render() |
| { |
| if (!m_layer) |
| return 0; |
| |
| const bool hasMsSupport = m_sgContext->renderContext()->supportsMultisampleTextures(); |
| const bool ssaaEnabled = hasMsSupport && m_layer->multisampleAAMode == QSSGRenderLayer::AAMode::SSAA; |
| const bool msaaEnabled = hasMsSupport && m_layer->multisampleAAMode > QSSGRenderLayer::AAMode::SSAA; |
| |
| m_sgContext->beginFrame(); |
| |
| // select correct fbo for aa |
| auto fbo = m_multisampleFbo ? m_multisampleFbo : m_fbo; |
| fbo = m_supersampleFbo ? m_supersampleFbo : fbo; |
| fbo = ssaaEnabled || msaaEnabled ? fbo : m_fbo; |
| |
| m_renderContext->setRenderTarget(fbo->fbo); |
| QSize surfaceSize = m_surfaceSize; |
| if (ssaaEnabled && m_supersampleFbo) |
| surfaceSize *= SSAA_Multiplier; |
| m_sgContext->setViewport(QRect(0, 0, surfaceSize.width(), surfaceSize.height())); |
| m_sgContext->setScissorRect(QRect()); |
| m_sgContext->setWindowDimensions(m_surfaceSize); |
| m_sgContext->setSceneColor(QColor(Qt::black)); |
| |
| m_sgContext->prepareLayerForRender(*m_layer); |
| m_sgContext->renderLayer(*m_layer, true); |
| |
| m_sgContext->endFrame(); |
| |
| if ((msaaEnabled && m_multisampleFbo) || (ssaaEnabled && m_supersampleFbo)) { |
| m_renderContext->setRenderTarget(m_fbo->fbo); |
| m_renderContext->setReadTarget(m_supersampleFbo ? m_supersampleFbo->fbo |
| : m_multisampleFbo->fbo); |
| if (m_supersampleFbo) { |
| m_renderContext->blitFramebuffer(0, 0, surfaceSize.width(), surfaceSize.height(), |
| 0, 0, m_surfaceSize.width(), m_surfaceSize.height(), |
| QSSGRenderClearValues::Color, |
| QSSGRenderTextureMagnifyingOp::Linear); |
| } else { |
| m_renderContext->blitFramebuffer(0, 0, m_surfaceSize.width(), m_surfaceSize.height(), |
| 0, 0, m_surfaceSize.width(), m_surfaceSize.height(), |
| QSSGRenderClearValues::Color, |
| QSSGRenderTextureMagnifyingOp::Nearest); |
| } |
| } |
| |
| if (dumpPerfTiming) { |
| if (++frameCount == 60) { |
| m_sgContext->performanceTimer()->dump(); |
| frameCount = 0; |
| } |
| } |
| |
| return HandleToID_cast(GLuint, size_t, m_fbo->color0->handle()); |
| } |
| |
| void QQuick3DSceneRenderer::render(const QRect &viewport, bool clearFirst) |
| { |
| if (!m_layer) |
| return; |
| |
| m_sgContext->beginFrame(); |
| |
| // set render target to be current window (default) |
| m_renderContext->setRenderTarget(nullptr); |
| |
| // set viewport |
| m_sgContext->setWindowDimensions(m_surfaceSize); |
| m_sgContext->setViewport(viewport); |
| m_sgContext->setScissorRect(viewport); |
| |
| // set clear color |
| m_sgContext->setSceneColor(QColor(Qt::black)); |
| |
| m_sgContext->prepareLayerForRender(*m_layer); |
| m_sgContext->renderLayer(*m_layer, clearFirst); |
| |
| m_sgContext->endFrame(); |
| |
| if (dumpPerfTiming) { |
| if (++frameCount == 60) { |
| m_sgContext->performanceTimer()->dump(); |
| frameCount = 0; |
| } |
| } |
| } |
| |
| void QQuick3DSceneRenderer::synchronize(QQuick3DViewport *item, const QSize &size, bool useFBO) |
| { |
| if (!item) |
| return; |
| |
| if (!m_renderStats) |
| m_renderStats = item->renderStats(); |
| |
| if (m_renderStats) |
| m_renderStats->startSync(); |
| |
| if (m_surfaceSize != size) { |
| m_layerSizeIsDirty = true; |
| m_surfaceSize = size; |
| } |
| |
| auto view3D = static_cast<QQuick3DViewport*>(item); |
| m_sceneManager = QQuick3DObjectPrivate::get(view3D->scene())->sceneManager; |
| m_sceneManager->updateDirtyNodes(); |
| |
| QQuick3DNode *importScene = view3D->importScene(); |
| if (importScene) |
| QQuick3DObjectPrivate::get(importScene)->sceneManager->updateDirtyNodes(); |
| |
| // Generate layer node |
| if (!m_layer) |
| m_layer = new QSSGRenderLayer(); |
| |
| // Update the layer node properties |
| updateLayerNode(view3D); |
| |
| // Set the root item for the scene to the layer |
| auto rootNode = static_cast<QSSGRenderNode*>(QQuick3DObjectPrivate::get(view3D->scene())->spatialNode); |
| if (rootNode != m_sceneRootNode) { |
| if (m_sceneRootNode) |
| removeNodeFromLayer(m_sceneRootNode); |
| |
| if (rootNode) |
| addNodeToLayer(rootNode); |
| |
| m_sceneRootNode = rootNode; |
| } |
| |
| // Add the referenced scene root node to the layer as well if available |
| QSSGRenderNode* importRootNode = nullptr; |
| if (importScene) { |
| importRootNode = static_cast<QSSGRenderNode*>( |
| QQuick3DObjectPrivate::get(importScene)->spatialNode); |
| } |
| if (importRootNode != m_importRootNode) { |
| if (m_importRootNode) |
| removeNodeFromLayer(m_importRootNode); |
| |
| if (importRootNode) |
| m_layer->addChildrenToLayer(*importRootNode); |
| |
| m_importRootNode = importRootNode; |
| } |
| |
| if (useFBO) { |
| if (!m_fbo || m_layerSizeIsDirty) { |
| if (m_fbo) |
| delete m_fbo; |
| |
| static const auto msaaModeSamples = [](QSSGRenderLayer::AAMode mode) -> int { |
| switch (mode) { |
| case QSSGRenderLayer::AAMode::X2: |
| return 2; |
| case QSSGRenderLayer::AAMode::X4: |
| case QSSGRenderLayer::AAMode::X8: |
| return 4; |
| case QSSGRenderLayer::AAMode::NoAA: |
| default: |
| break; |
| } |
| return 1; |
| }; |
| |
| const bool hasMsSupport = m_sgContext->renderContext()->supportsMultisampleTextures(); |
| const auto msaaMode = hasMsSupport ? m_layer->multisampleAAMode : QSSGRenderLayer::AAMode::NoAA; |
| const auto samples = msaaModeSamples(msaaMode); |
| if (samples > 1) { |
| m_multisampleFbo = new FramebufferObject(m_surfaceSize, m_renderContext, samples); |
| } else if (msaaMode == QSSGRenderLayer::AAMode::SSAA) { |
| m_supersampleFbo = new FramebufferObject(m_surfaceSize * SSAA_Multiplier, |
| m_renderContext); |
| } |
| m_fbo = new FramebufferObject(m_surfaceSize, m_renderContext); |
| m_layerSizeIsDirty = false; |
| } |
| } |
| |
| if (m_renderStats) |
| m_renderStats->endSync(m_sgContext, dumpRenderTimes); |
| } |
| |
| void QQuick3DSceneRenderer::update() |
| { |
| if (data) |
| static_cast<SGFramebufferObjectNode *>(data)->scheduleRender(); |
| } |
| |
| void QQuick3DSceneRenderer::invalidateFramebufferObject() |
| { |
| if (data) |
| static_cast<SGFramebufferObjectNode *>(data)->invalidatePending = true; |
| } |
| |
| QSSGRenderPickResult QQuick3DSceneRenderer::pick(const QPointF &pos) |
| { |
| return m_sgContext->renderer()->pick(*m_layer, QVector2D(m_surfaceSize.width(), m_surfaceSize.height()), QVector2D(float(pos.x()), float(pos.y()))); |
| } |
| |
| QSSGRenderPickResult QQuick3DSceneRenderer::syncPick(const QPointF &pos) |
| { |
| return m_sgContext->renderer()->syncPick(*m_layer, QVector2D(m_surfaceSize.width(), m_surfaceSize.height()), QVector2D(float(pos.x()), float(pos.y()))); |
| } |
| |
| QQuick3DRenderStats *QQuick3DSceneRenderer::renderStats() |
| { |
| return m_renderStats; |
| } |
| |
| void QQuick3DSceneRenderer::updateLayerNode(QQuick3DViewport *view3D) |
| { |
| QSSGRenderLayer *layerNode = m_layer; |
| layerNode->progressiveAAMode = QSSGRenderLayer::AAMode(view3D->environment()->progressiveAAMode()); |
| layerNode->multisampleAAMode = QSSGRenderLayer::AAMode(view3D->environment()->multisampleAAMode()); |
| layerNode->temporalAAEnabled = view3D->environment()->temporalAAEnabled(); |
| |
| layerNode->background = QSSGRenderLayer::Background(view3D->environment()->backgroundMode()); |
| layerNode->clearColor = QVector3D(float(view3D->environment()->clearColor().redF()), |
| float(view3D->environment()->clearColor().greenF()), |
| float(view3D->environment()->clearColor().blueF())); |
| |
| layerNode->m_width = 100.f; |
| layerNode->m_height = 100.f; |
| layerNode->widthUnits = QSSGRenderLayer::UnitType::Percent; |
| layerNode->heightUnits = QSSGRenderLayer::UnitType::Percent; |
| |
| layerNode->aoStrength = view3D->environment()->aoStrength(); |
| layerNode->aoDistance = view3D->environment()->aoDistance(); |
| layerNode->aoSoftness = view3D->environment()->aoSoftness(); |
| layerNode->aoBias = view3D->environment()->aoBias(); |
| layerNode->aoSamplerate = view3D->environment()->aoSampleRate(); |
| layerNode->aoDither = view3D->environment()->aoDither(); |
| |
| // ### These images will not be registered anywhere |
| if (view3D->environment()->lightProbe()) |
| layerNode->lightProbe = view3D->environment()->lightProbe()->getRenderImage(); |
| else |
| layerNode->lightProbe = nullptr; |
| |
| layerNode->probeBright = view3D->environment()->probeBrightness(); |
| layerNode->fastIbl = view3D->environment()->fastImageBasedLightingEnabled(); |
| layerNode->probeHorizon = view3D->environment()->probeHorizon(); |
| layerNode->probeFov = view3D->environment()->probeFieldOfView(); |
| |
| layerNode->lightProbe2 = nullptr; |
| |
| if (view3D->camera()) |
| layerNode->activeCamera = view3D->camera()->cameraNode(); |
| |
| if (view3D->environment()->depthTestEnabled()) |
| layerNode->flags.setFlag(QSSGRenderNode::Flag::LayerEnableDepthTest, true); |
| else |
| layerNode->flags.setFlag(QSSGRenderNode::Flag::LayerEnableDepthTest, false); |
| |
| if (view3D->environment()->depthPrePassEnabled()) |
| layerNode->flags.setFlag(QSSGRenderNode::Flag::LayerEnableDepthPrePass, true); |
| else |
| layerNode->flags.setFlag(QSSGRenderNode::Flag::LayerEnableDepthPrePass, false); |
| |
| layerNode->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty); |
| } |
| |
| void QQuick3DSceneRenderer::removeNodeFromLayer(QSSGRenderNode *node) |
| { |
| if (!m_layer) |
| return; |
| |
| m_layer->removeChild(*node); |
| } |
| |
| void QQuick3DSceneRenderer::addNodeToLayer(QSSGRenderNode *node) |
| { |
| if (!m_layer) |
| return; |
| |
| m_layer->addChild(*node); |
| } |
| |
| QQuick3DSceneRenderer::FramebufferObject::FramebufferObject(const QSize &s, const QSSGRef<QSSGRenderContext> &context, int msaaSamples) |
| { |
| size = s; |
| renderContext = context; |
| samples = renderContext->supportsMultisampleTextures() ? msaaSamples : -1; |
| |
| depthStencil = new QSSGRenderTexture2D(renderContext); |
| if (samples > 1) |
| depthStencil->setTextureDataMultisample(samples, size.width(), size.height(), QSSGRenderTextureFormat::Depth24Stencil8); |
| else |
| depthStencil->setTextureData(QSSGByteView(), 0, size.width(), size.height(), QSSGRenderTextureFormat::Depth24Stencil8); |
| color0 = new QSSGRenderTexture2D(renderContext); |
| if (samples > 1) |
| color0->setTextureDataMultisample(samples, size.width(), size.height(), QSSGRenderTextureFormat::RGBA8); |
| else |
| color0->setTextureData(QSSGByteView(), 0, size.width(), size.height(), QSSGRenderTextureFormat::RGBA8); |
| fbo = new QSSGRenderFrameBuffer(renderContext); |
| fbo->attach(QSSGRenderFrameBufferAttachment::Color0, color0, color0->target()); |
| fbo->attach(QSSGRenderFrameBufferAttachment::DepthStencil, depthStencil, depthStencil->target()); |
| } |
| |
| QQuick3DSceneRenderer::FramebufferObject::~FramebufferObject() |
| { |
| |
| } |
| |
| QSGRenderNode::StateFlags QQuick3DSGRenderNode::changedStates() const |
| { |
| return BlendState | StencilState | DepthState | ScissorState | ColorState | CullState | ViewportState | RenderTargetState; |
| } |
| |
| namespace { |
| QRect convertQtRectToGLViewport(const QRectF &rect, const QSize surfaceSize) { |
| // |
| const int x = int(rect.x()); |
| const int y = surfaceSize.height() - (int(rect.y()) + int(rect.height())); |
| const int width = int(rect.width()); |
| const int height = int(rect.height()); |
| return QRect(x, y, width, height); |
| } |
| } |
| |
| void QQuick3DSGRenderNode::render(const QSGRenderNode::RenderState *state) |
| { |
| if (renderer->renderStats()) |
| renderer->renderStats()->startRender(); |
| |
| Q_UNUSED(state) |
| // calculate viewport |
| const double dpr = renderer->m_window->devicePixelRatio(); |
| const QSizeF itemSize = renderer->surfaceSize() / dpr; |
| |
| QRectF viewport = matrix()->mapRect(QRectF(QPoint(0, 0), itemSize)); |
| viewport = QRectF(viewport.topLeft() * dpr, viewport.size() * dpr); |
| |
| // render |
| renderer->render(convertQtRectToGLViewport(viewport, window->size() * dpr)); |
| markDirty(QSGNode::DirtyMaterial); |
| |
| // reset some state |
| cleanupOpenGLState(); |
| |
| if (renderer->renderStats()) { |
| if (dumpRenderTimes) |
| QOpenGLContext::currentContext()->functions()->glFinish(); |
| renderer->renderStats()->endRender(dumpRenderTimes); |
| } |
| if (renderer->m_sgContext->renderer()->rendererRequestsFrames()) |
| window->update(); |
| } |
| |
| void QQuick3DSGRenderNode::releaseResources() |
| { |
| } |
| |
| QSGRenderNode::RenderingFlags QQuick3DSGRenderNode::flags() const |
| { |
| return QSGRenderNode::RenderingFlags(); |
| } |
| |
| QQuick3DSGDirectRenderer::QQuick3DSGDirectRenderer(QQuick3DSceneRenderer *renderer, QQuickWindow *window, QQuick3DSGDirectRenderer::QQuick3DSGDirectRendererMode mode) |
| : m_renderer(renderer) |
| , m_window(window) |
| , m_mode(mode) |
| { |
| #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) |
| if (QSGRendererInterface::isApiRhiBased(window->rendererInterface()->graphicsApi())) { |
| if (mode == Underlay) |
| connect(window, &QQuickWindow::beforeRenderPassRecording, this, &QQuick3DSGDirectRenderer::render, Qt::DirectConnection); |
| else |
| connect(window, &QQuickWindow::afterRenderPassRecording, this, &QQuick3DSGDirectRenderer::render, Qt::DirectConnection); |
| } else |
| #endif |
| { |
| if (mode == Underlay) |
| connect(window, &QQuickWindow::beforeRendering, this, &QQuick3DSGDirectRenderer::render, Qt::DirectConnection); |
| else |
| connect(window, &QQuickWindow::afterRendering, this, &QQuick3DSGDirectRenderer::render, Qt::DirectConnection); |
| } |
| } |
| |
| QQuick3DSGDirectRenderer::~QQuick3DSGDirectRenderer() |
| { |
| delete m_renderer; |
| } |
| |
| void QQuick3DSGDirectRenderer::setViewport(const QRectF &viewport) |
| { |
| m_viewport = viewport; |
| } |
| |
| void QQuick3DSGDirectRenderer::requestRender() |
| { |
| m_window->update(); |
| } |
| |
| void QQuick3DSGDirectRenderer::render() |
| { |
| #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) |
| m_window->beginExternalCommands(); |
| #endif |
| |
| if (m_renderer->renderStats()) |
| m_renderer->renderStats()->startRender(); |
| |
| const QRect glViewport = convertQtRectToGLViewport(m_viewport, m_window->size() * m_window->devicePixelRatio()); |
| m_renderer->render(glViewport, m_mode == Underlay); |
| cleanupOpenGLState(); |
| |
| if (m_renderer->renderStats()) { |
| if (dumpRenderTimes) |
| QOpenGLContext::currentContext()->functions()->glFinish(); |
| m_renderer->renderStats()->endRender(dumpRenderTimes); |
| } |
| if (m_renderer->m_sgContext->renderer()->rendererRequestsFrames()) |
| m_window->update(); |
| |
| #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) |
| m_window->endExternalCommands(); |
| #endif |
| } |
| |
| QT_END_NAMESPACE |