diff --git a/.gitattributes b/.gitattributes index 0909f7a01df6d400f7b8fd5ad41c4eb68d29efc7..018cf2cbcb93d7f02744f707f325802bd3b5a364 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ .tag export-subst +.gitignore export-ignore .gitattributes export-ignore diff --git a/src/imports/qtcanvas3d/canvas3d.cpp b/src/imports/qtcanvas3d/canvas3d.cpp index d7de4931bc68c833226efccc2713bf5941950c41..39acb80f18c17df34aab76cd1d20c087b93d43ca 100644 --- a/src/imports/qtcanvas3d/canvas3d.cpp +++ b/src/imports/qtcanvas3d/canvas3d.cpp @@ -160,7 +160,6 @@ void Canvas::setWidth(int width) << maxWidth; newWidth = maxWidth; } - QQuickItem::setWidth(qreal(newWidth)); } @@ -454,7 +453,6 @@ void Canvas::setPixelSize(QSize pixelSize) qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__ << "(pixelSize:" << pixelSize << ")"; - if (pixelSize.width() > m_maxSize.width()) { qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__ << "():" @@ -471,6 +469,11 @@ void Canvas::setPixelSize(QSize pixelSize) pixelSize.setHeight(m_maxSize.height()); } + if (pixelSize.width() <= 0) + pixelSize.setWidth(1); + if (pixelSize.height() <= 0) + pixelSize.setHeight(1); + if (m_fboSize == pixelSize) return; @@ -583,6 +586,10 @@ bool Canvas::firstSync() if (!m_renderer->qtContextResolved()) { m_firstSync = false; QSize initializedSize = boundingRect().size().toSize(); + if (initializedSize.width() <= 0) + initializedSize.setWidth(1); + if (initializedSize.height() <= 0) + initializedSize.setHeight(1); m_renderer->resolveQtContext(window(), initializedSize, m_renderTarget); m_isOpenGLES2 = m_renderer->isOpenGLES2(); @@ -757,9 +764,9 @@ void Canvas::queueNextRender() updateWindowParameters(); // Don't try to do anything before the renderer/node are ready - if (!m_rendererReady) { + if (!m_rendererReady || !window()) { qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__ - << " Renderer not ready, returning"; + << " Renderer or window not ready, returning"; return; } diff --git a/src/imports/qtcanvas3d/context3d.cpp b/src/imports/qtcanvas3d/context3d.cpp index 1592dc3f2ccc462ea275d6333432fa6c8467097e..829d5a90bed6e293f8b629435327946c8d6e036b 100644 --- a/src/imports/qtcanvas3d/context3d.cpp +++ b/src/imports/qtcanvas3d/context3d.cpp @@ -5754,19 +5754,25 @@ void CanvasContext::scheduleSyncCommand(GlSyncCommand *command) if (m_canvas->window() && m_canvas->renderer()) { QOpenGLContext *ctx = m_canvas->window()->openglContext(); if (ctx) { + bool jobDeleted = false; if (ctx->thread() != QThread::currentThread()) { // In case of threaded renderer, we block the main thread until the job is done CanvasRenderJob *syncJob = new CanvasRenderJob(command, &m_renderJobMutex, &m_renderJobCondition, - m_canvas->renderer()); + m_canvas->renderer(), + &jobDeleted); m_renderJobMutex.lock(); m_canvas->window()->scheduleRenderJob(syncJob, QQuickWindow::NoStage); - m_renderJobCondition.wait(&m_renderJobMutex); + // scheduleRenderJob will delete the job if the window is not exposed + if (!jobDeleted) + m_renderJobCondition.wait(&m_renderJobMutex); m_renderJobMutex.unlock(); + } else { // In case of non-threaded renderer, scheduling a job executes it immediately, // so we don't need synchronization. - CanvasRenderJob *syncJob = new CanvasRenderJob(command, 0, 0, m_canvas->renderer()); + CanvasRenderJob *syncJob = new CanvasRenderJob(command, 0, 0, m_canvas->renderer(), + &jobDeleted); m_canvas->window()->scheduleRenderJob(syncJob, QQuickWindow::NoStage); } } diff --git a/src/imports/qtcanvas3d/renderjob.cpp b/src/imports/qtcanvas3d/renderjob.cpp index dfb891b7a222b87d5093f55405f448cfe11bcbfb..754aacb0f91f27832e275fe05befd037bc41b777 100644 --- a/src/imports/qtcanvas3d/renderjob.cpp +++ b/src/imports/qtcanvas3d/renderjob.cpp @@ -49,14 +49,26 @@ QT_CANVAS3D_BEGIN_NAMESPACE */ CanvasRenderJob::CanvasRenderJob(GlSyncCommand *command, QMutex *mutex, QWaitCondition *condition, - CanvasRenderer *renderer) - : m_command(command), m_mutex(mutex), m_condition(condition), m_renderer(renderer) + CanvasRenderer *renderer, bool *deleted) + : m_command(command), + m_mutex(mutex), + m_condition(condition), + m_renderer(renderer), + m_deleted(deleted), + m_creationThread(QThread::currentThread()) { } CanvasRenderJob::~CanvasRenderJob() { - notifyGuiThread(); + if (m_creationThread != QThread::currentThread()) { + notifyGuiThread(); + } else { + // If the job is deleted in the thread it was created in, we can assume the scheduling + // failed and notify the context that we got deleted via the m_deleted flag, + // so it won't start waiting for job to complete. + *m_deleted = true; + } } void CanvasRenderJob::run() diff --git a/src/imports/qtcanvas3d/renderjob_p.h b/src/imports/qtcanvas3d/renderjob_p.h index 7d0e50ba531c317415f1d4284df9b4026d843841..e4c0ff1703ce5c8ae603ce37d034515140e362ec 100644 --- a/src/imports/qtcanvas3d/renderjob_p.h +++ b/src/imports/qtcanvas3d/renderjob_p.h @@ -53,6 +53,7 @@ #include <QtCore/QWaitCondition> #include <QtCore/QMutex> #include <QtCore/QRunnable> +#include <QtCore/QThread> QT_BEGIN_NAMESPACE QT_CANVAS3D_BEGIN_NAMESPACE @@ -63,7 +64,8 @@ class CanvasRenderJob : public QRunnable { public: CanvasRenderJob(GlSyncCommand *command, QMutex *mutex, - QWaitCondition *condition, QtCanvas3D::CanvasRenderer *renderer); + QWaitCondition *condition, QtCanvas3D::CanvasRenderer *renderer, + bool *deleted); ~CanvasRenderJob(); void run(); @@ -76,6 +78,8 @@ private: QMutex *m_mutex; QWaitCondition *m_condition; CanvasRenderer *m_renderer; + bool *m_deleted; + QThread *m_creationThread; }; QT_CANVAS3D_END_NAMESPACE diff --git a/tests/auto/qmltest/canvas3d/tst_render_dynamic.qml b/tests/auto/qmltest/canvas3d/tst_render_dynamic.qml index 80b9e3aa58b99a490eccdde389b5d5dcd298add3..2f4da23839d2bafbb3d2c8f57be486505bf08ca3 100644 --- a/tests/auto/qmltest/canvas3d/tst_render_dynamic.qml +++ b/tests/auto/qmltest/canvas3d/tst_render_dynamic.qml @@ -72,6 +72,7 @@ Item { import QtCanvas3D 1.0 import QtQuick.Window 2.0 Window { + property alias windowCanvas: theCanvas visible: true width: 300 height: 300 @@ -151,20 +152,28 @@ Item { tryCompare(top, "initOk", true) tryCompare(top, "renderOk", true) tryCompare(top, "windowHidden", false) + canvasWindow.destroy() + tryCompare(top, "canvasWindow", null) } function test_render_5_dynamic_window_hide_and_reshow() { - verify(canvasWindow !== null) + initOk = false + renderOk = false + verify(canvasWindow === null) verify(windowHidden === false) + createCanvasWindow() + verify(canvasWindow !== null) + tryCompare(top, "initOk", true) + tryCompare(top, "renderOk", true) + tryCompare(top, "windowHidden", false) + waitForRendering(canvasWindow.windowCanvas) canvasWindow.hide() tryCompare(top, "windowHidden", true) renderOk = false canvasWindow.show() + waitForRendering(canvasWindow.windowCanvas) tryCompare(top, "renderOk", true) tryCompare(top, "windowHidden", false) - } - function test_render_6_dynamic_window_destroy() { - verify(canvasWindow !== null) canvasWindow.destroy() tryCompare(top, "canvasWindow", null) } diff --git a/tests/auto/qmltest/canvas3d/tst_render_simple.js b/tests/auto/qmltest/canvas3d/tst_render_simple.js index e8565ab56cf969e8f00a7e68d0ad63c2c2ee36a1..cd2c840f937fd31df6a37486fef3f111342c0c44 100644 --- a/tests/auto/qmltest/canvas3d/tst_render_simple.js +++ b/tests/auto/qmltest/canvas3d/tst_render_simple.js @@ -82,6 +82,28 @@ function initializeGL(canvas) { function paintGL(canvas) { gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 3); + return (gl.getError() === 0); +} + +function resizeGL(canvas) +{ + var pixelRatio = canvas.devicePixelRatio; + canvas.pixelSize = Qt.size(canvas.width * pixelRatio, + canvas.height * pixelRatio); + + if (gl) { + gl.viewport(0, 0, + canvas.width * canvas.devicePixelRatio, + canvas.height * canvas.devicePixelRatio); + } +} + +function getWidth() { + return gl.drawingBufferWidth +} + +function getHeight() { + return gl.drawingBufferHeight } function initShaders() diff --git a/tests/auto/qmltest/canvas3d/tst_render_simple.qml b/tests/auto/qmltest/canvas3d/tst_render_simple.qml index 9d25dd3dbefab606113cbe308983c3e9bfd1166c..3a714082e8756ba93a1ec0a81181e4c16f2e08a7 100644 --- a/tests/auto/qmltest/canvas3d/tst_render_simple.qml +++ b/tests/auto/qmltest/canvas3d/tst_render_simple.qml @@ -51,12 +51,13 @@ Item { property bool widthChanged: false property bool initOk: false property bool renderOk: false - anchors.fill: parent + height: 300 + width: 300 onInitializeGL: initOk = Content.initializeGL(render_simple) onPaintGL: { - Content.paintGL(render_simple) - renderOk = true + renderOk = Content.paintGL(render_simple) } + onResizeGL: Content.resizeGL(render_simple) onHeightChanged: heightChanged = true onWidthChanged: widthChanged = true } @@ -75,18 +76,42 @@ Item { render_simple.heightChanged = false render_simple.widthChanged = false render_simple.renderOk = false - top.height = 200 + render_simple.height = 200 waitForRendering(render_simple) verify(render_simple.heightChanged === true) verify(render_simple.widthChanged === false) tryCompare(render_simple, "renderOk", true) + var fboHeight = Content.getHeight() + var fboWidth = Content.getWidth() + verify(fboHeight === render_simple.height) + verify(fboWidth === render_simple.width) render_simple.renderOk = false - top.width = 200 + render_simple.width = 200 waitForRendering(render_simple) verify(render_simple.heightChanged === true) verify(render_simple.widthChanged === true) tryCompare(render_simple, "renderOk", true) + fboHeight = Content.getHeight() + fboWidth = Content.getWidth() + verify(fboHeight === render_simple.height) + verify(fboWidth === render_simple.width) + + render_simple.heightChanged = false + render_simple.widthChanged = false + render_simple.width = 0 + render_simple.height = 0 + waitForRendering(render_simple) + verify(render_simple.heightChanged === true) + verify(render_simple.widthChanged === true) + render_simple.renderOk = false + waitForRendering(render_simple) + tryCompare(render_simple, "renderOk", true) + fboHeight = Content.getHeight() + fboWidth = Content.getWidth() + // Zero size canvas will still create 1x1 fbo + verify(fboHeight === 1) + verify(fboWidth === 1) } } }