aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/quick/scenegraph
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-09-03 13:27:57 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2021-09-05 22:20:26 +0200
commitb407d22654c19ca30e73aa5910f73c78c829fa62 (patch)
tree65dab1f8ef86905ddfec2e33e714e2a8d4d34062 /tests/auto/quick/scenegraph
parent4de27382f9b5f05b8d5821247ea7653b44264c03 (diff)
Add autotest for QSGPlainTexture resizing
Builds on the infrastructure added in 0bf9faf0906de18d4fa8f36d99708b4a79df17e8 so this cannot go to 6.2. Task-number: QTBUG-96190 Change-Id: Ibb9b7ad284c6794c33a19dca6237778c32b055b8 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'tests/auto/quick/scenegraph')
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp190
1 files changed, 130 insertions, 60 deletions
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
index 206eee5b16..7be1d1d9f1 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -44,6 +44,7 @@
#include <private/qsgcontext_p.h>
#include <private/qsgrenderloop_p.h>
#include <private/qsgrhisupport_p.h>
+#include <private/qsgplaintexture_p.h>
#include "../../shared/util.h"
#include "../shared/visualtestutil.h"
@@ -116,6 +117,7 @@ private slots:
void createTextureFromImage_data();
void createTextureFromImage();
void withAdoptedRhi();
+ void resizeTextureFromImage();
private:
QQuickView *createView(const QString &file, QWindow *parent = nullptr, int x = -1, int y = -1, int w = -1, int h = -1);
@@ -542,6 +544,10 @@ void tst_SceneGraph::createTextureFromImage()
QCOMPARE(texture->hasAlphaChannel(), expectedAlpha);
}
+#if QT_CONFIG(vulkan)
+static QVulkanInstance *TestOffscreenScene_vkinst = nullptr;
+#endif
+
struct TestOffscreenScene
{
QQuickRenderControl *renderControl = nullptr;
@@ -549,77 +555,89 @@ struct TestOffscreenScene
QQmlEngine *engine = nullptr;
QQmlComponent *component = nullptr;
QQuickItem *rootItem = nullptr;
-};
-static void destroyOffscreenScene(const TestOffscreenScene &scene)
-{
- delete scene.component;
- delete scene.engine;
- delete scene.window;
- delete scene.renderControl;
-}
+ // to be called at the end of each test case once all TestOffscreenScene instances are destroyed
+ static void cleanup()
+ {
+#if QT_CONFIG(vulkan)
+ delete TestOffscreenScene_vkinst;
+ TestOffscreenScene_vkinst = nullptr;
+#endif
+ }
+
+ ~TestOffscreenScene()
+ {
+ delete component;
+ delete engine;
+ delete window;
+ delete renderControl;
+ }
+};
-static TestOffscreenScene createOffscreenScene(const QUrl &url, QQuickWindow *compatibleWindow = nullptr)
+static TestOffscreenScene *createOffscreenScene(const QUrl &url, QQuickWindow *compatibleWindow = nullptr)
{
- TestOffscreenScene scene;
- scene.renderControl = new QQuickRenderControl;
- scene.window = new QQuickWindow(scene.renderControl);
+ std::unique_ptr<TestOffscreenScene> scene(new TestOffscreenScene);
+ scene->renderControl = new QQuickRenderControl;
+ scene->window = new QQuickWindow(scene->renderControl);
if (compatibleWindow) {
- scene.window->setGraphicsApi(compatibleWindow->rendererInterface()->graphicsApi());
+ scene->window->setGraphicsApi(compatibleWindow->rendererInterface()->graphicsApi());
#if QT_CONFIG(vulkan)
if (compatibleWindow->rendererInterface()->graphicsApi() == QSGRendererInterface::VulkanRhi)
- scene.window->setVulkanInstance(compatibleWindow->vulkanInstance());
+ scene->window->setVulkanInstance(compatibleWindow->vulkanInstance());
#endif
QRhi *rhi = static_cast<QRhi *>(compatibleWindow->rendererInterface()->getResource(compatibleWindow, QSGRendererInterface::RhiResource));
if (rhi) {
// make it so that the rendercontrol will not create a new QRhi, but rather use what we specify here
- scene.window->setGraphicsDevice(QQuickGraphicsDevice::fromRhi(rhi));
+ scene->window->setGraphicsDevice(QQuickGraphicsDevice::fromRhi(rhi));
} else {
qWarning("No QRhi from the specified compatibleWindow, this is unexpected");
}
} else {
#if QT_CONFIG(vulkan)
- if (QQuickWindow::graphicsApi() == QSGRendererInterface::Vulkan) // honor what QSG_RHI_BACKEND says
- scene.window->setVulkanInstance(QSGRhiSupport::defaultVulkanInstance());
+ if (QQuickWindow::graphicsApi() == QSGRendererInterface::Vulkan) { // honor what QSG_RHI_BACKEND says
+ if (!TestOffscreenScene_vkinst) {
+ TestOffscreenScene_vkinst = new QVulkanInstance;
+ TestOffscreenScene_vkinst->setExtensions(QQuickGraphicsConfiguration::preferredInstanceExtensions());
+ TestOffscreenScene_vkinst->create();
+ }
+ scene->window->setVulkanInstance(TestOffscreenScene_vkinst);
+ }
#endif
}
- scene.engine = new QQmlEngine;
- scene.component = new QQmlComponent(scene.engine, url);
- if (scene.component->isError()) {
- for (const QQmlError &error : scene.component->errors())
+ scene->engine = new QQmlEngine;
+ scene->component = new QQmlComponent(scene->engine, url);
+ if (scene->component->isError()) {
+ for (const QQmlError &error : scene->component->errors())
qWarning() << error.url() << error.line() << error;
- destroyOffscreenScene(scene);
- return {};
+ return nullptr;
}
- QObject *rootObject = scene.component->create();
- if (scene.component->isError()) {
- for (const QQmlError &error : scene.component->errors())
+ QObject *rootObject = scene->component->create();
+ if (scene->component->isError()) {
+ for (const QQmlError &error : scene->component->errors())
qWarning() << error.url() << error.line() << error;
}
- scene.rootItem = qobject_cast<QQuickItem *>(rootObject);
- if (!scene.rootItem) {
+ scene->rootItem = qobject_cast<QQuickItem *>(rootObject);
+ if (!scene->rootItem) {
qWarning("No root QQuickItem");
- destroyOffscreenScene(scene);
- return {};
+ return nullptr;
}
- scene.window->contentItem()->setSize(scene.rootItem->size());
- scene.window->setGeometry(0, 0, scene.rootItem->width(), scene.rootItem->height());
- scene.rootItem->setParentItem(scene.window->contentItem());
+ scene->window->contentItem()->setSize(scene->rootItem->size());
+ scene->window->setGeometry(0, 0, scene->rootItem->width(), scene->rootItem->height());
+ scene->rootItem->setParentItem(scene->window->contentItem());
- if (!scene.renderControl->initialize()) {
+ if (!scene->renderControl->initialize()) {
qWarning("Failed to initialize rendercontrol");
- destroyOffscreenScene(scene);
- return {};
+ return nullptr;
}
- return scene;
+ return scene.release();
}
void tst_SceneGraph::withAdoptedRhi()
@@ -631,19 +649,19 @@ void tst_SceneGraph::withAdoptedRhi()
// test. QQuickView would not be suitable because it it uses the threaded
// render loop, then we end up in threading issues with graphics resources.
- TestOffscreenScene scene1 = createOffscreenScene(testFileUrl(QLatin1String("renderControl_rect.qml")));
- QVERIFY(scene1.renderControl && scene1.window && scene1.rootItem);
+ TestOffscreenScene *scene1 = createOffscreenScene(testFileUrl(QLatin1String("renderControl_rect.qml")));
+ QVERIFY(scene1->renderControl && scene1->window && scene1->rootItem);
// Now another one, but this time sharing the same QRhi as the first one.
- TestOffscreenScene scene2 = createOffscreenScene(testFileUrl(QLatin1String("renderControl_rect.qml")), scene1.window);
- QVERIFY(scene2.renderControl && scene2.window && scene2.rootItem);
+ TestOffscreenScene *scene2 = createOffscreenScene(testFileUrl(QLatin1String("renderControl_rect.qml")), scene1->window);
+ QVERIFY(scene2->renderControl && scene2->window && scene2->rootItem);
- QRhi *rhi = static_cast<QRhi *>(scene1.window->rendererInterface()->getResource(scene1.window, QSGRendererInterface::RhiResource));
- QCOMPARE(rhi, static_cast<QRhi *>(scene2.window->rendererInterface()->getResource(scene2.window, QSGRendererInterface::RhiResource)));
+ QRhi *rhi = static_cast<QRhi *>(scene1->window->rendererInterface()->getResource(scene1->window, QSGRendererInterface::RhiResource));
+ QCOMPARE(rhi, static_cast<QRhi *>(scene2->window->rendererInterface()->getResource(scene2->window, QSGRendererInterface::RhiResource)));
{ // scope to get resources destroyed before the QRhi
- const QSize size = scene1.rootItem->size().toSize();
- QCOMPARE(size, scene2.rootItem->size().toSize());
+ const QSize size = scene1->rootItem->size().toSize();
+ QCOMPARE(size, scene2->rootItem->size().toSize());
QScopedPointer<QRhiRenderBuffer> ds(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size, 1));
QVERIFY(ds->create());
@@ -657,7 +675,7 @@ void tst_SceneGraph::withAdoptedRhi()
QScopedPointer<QRhiRenderPassDescriptor> rp1(texRt1->newCompatibleRenderPassDescriptor());
texRt1->setRenderPassDescriptor(rp1.data());
QVERIFY(texRt1->create());
- scene1.window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(texRt1.data()));
+ scene1->window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(texRt1.data()));
// for scene2
QScopedPointer<QRhiTexture> tex2(rhi->newTexture(QRhiTexture::RGBA8, size, 1,
@@ -669,20 +687,20 @@ void tst_SceneGraph::withAdoptedRhi()
QScopedPointer<QRhiRenderPassDescriptor> rp2(texRt2->newCompatibleRenderPassDescriptor());
texRt2->setRenderPassDescriptor(rp2.data());
QVERIFY(texRt2->create());
- scene2.window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(texRt2.data()));
+ scene2->window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(texRt2.data()));
// render a frame, first with scene1, then with scene2, targeting their respective textures
- scene1.renderControl->polishItems();
- scene1.renderControl->beginFrame();
- scene1.renderControl->sync();
- scene1.renderControl->render();
- scene1.renderControl->endFrame();
-
- scene2.renderControl->polishItems();
- scene2.renderControl->beginFrame();
- scene2.renderControl->sync();
- scene2.renderControl->render();
- scene2.renderControl->endFrame();
+ scene1->renderControl->polishItems();
+ scene1->renderControl->beginFrame();
+ scene1->renderControl->sync();
+ scene1->renderControl->render();
+ scene1->renderControl->endFrame();
+
+ scene2->renderControl->polishItems();
+ scene2->renderControl->beginFrame();
+ scene2->renderControl->sync();
+ scene2->renderControl->render();
+ scene2->renderControl->endFrame();
// Both tex1 and tex2 belong to the same one QRhi. Read back the
// contents. In a real world application one could now render with the
@@ -718,10 +736,62 @@ void tst_SceneGraph::withAdoptedRhi()
}
}
- destroyOffscreenScene(scene2); // this does not destroy the QRhi
+ delete scene2; // this does not destroy the QRhi
// call anything on the QRhi just to test if it is still valid
QVERIFY(!rhi->isDeviceLost());
- destroyOffscreenScene(scene1); // this does
+ delete scene1; // this does
+
+ TestOffscreenScene::cleanup();
+}
+
+static inline void commitTexture(QRhi *rhi, QSGTexture *texture)
+{
+ QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
+ texture->commitTextureOperations(rhi, rub);
+ QRhiCommandBuffer *cb = nullptr;
+ rhi->beginOffscreenFrame(&cb);
+ cb->resourceUpdate(rub);
+ rhi->endOffscreenFrame();
+}
+
+void tst_SceneGraph::resizeTextureFromImage()
+{
+ if (!isRunningOnRhi())
+ QSKIP("Skipping test due to not running with QRhi");
+
+ // We will need to directly work with QSGTexture and QRhi so have
+ // to be on the same thread as the scene graph. Hence using the
+ // offscreen infrastructure from other tests.
+
+ // note the lifetimes: (vulkan instance) > scenegraph(incl. QRhi) > QSGTexture
+ {
+ QScopedPointer<TestOffscreenScene> scene(createOffscreenScene(testFileUrl(QLatin1String("renderControl_rect.qml"))));
+ QVERIFY(scene->renderControl && scene->window && scene->rootItem);
+
+ {
+ QImage image(256, 128, QImage::Format_RGBA8888);
+ QScopedPointer<QSGTexture> texture(scene->window->createTextureFromImage(image, QQuickWindow::TextureHasAlphaChannel));
+ QRhi *rhi = static_cast<QRhi *>(scene->window->rendererInterface()->getResource(scene->window, QSGRendererInterface::RhiResource));
+ QVERIFY(rhi);
+ commitTexture(rhi, texture.data());
+ // neither is too big nor relies on optional features like NPoT repeat so the size should match always
+ QCOMPARE(texture->textureSize(), image.size());
+ QCOMPARE(texture->rhiTexture()->pixelSize(), image.size());
+
+ QSGPlainTexture *plainTex = qobject_cast<QSGPlainTexture *>(texture.data());
+ QVERIFY(plainTex);
+
+ // QTBUG-96190 - the commitTexture call here used to crash due to not
+ // updating the QRhiTexture size correctly in QSGPlainTexture.
+ image = QImage(512, 256, QImage::Format_RGBA8888);
+ plainTex->setImage(image);
+ commitTexture(rhi, texture.data());
+ QCOMPARE(texture->textureSize(), image.size());
+ QCOMPARE(texture->rhiTexture()->pixelSize(), image.size());
+ }
+ }
+
+ TestOffscreenScene::cleanup();
}
bool tst_SceneGraph::isRunningOnRhi()