summaryrefslogtreecommitdiffstats
path: root/tests/auto/gui/qvulkan
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2017-02-12 15:08:52 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2017-03-22 15:40:57 +0000
commit8d72aba9e3358fdf51578533ab7f46602f7fd103 (patch)
tree5e6ff0ec0ee399bf343654fc9280012eafa29a3d /tests/auto/gui/qvulkan
parent66e162e8f19f8a1f9b4f96008f2853be7fc1822e (diff)
Introduce QVulkanWindow
A convenience subclass of QWindow that provides a Vulkan-capable window with a double-buffered FIFO swapchain. While advanced use cases are better served by a custom QWindow subclass, many applications can benefit from having a convenient helper that makes getting started easier. Add also three examples of increasing complexity, and a variant that shows embeddeding into widgets via QWindowContainer. [ChangeLog][QtGui] Added QVulkanWindow, a convenience subclass of QWindow. Task-number: QTBUG-55981 Change-Id: I6cdc9ff1390ac6258e278377233fd369a0bfeddc Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'tests/auto/gui/qvulkan')
-rw-r--r--tests/auto/gui/qvulkan/tst_qvulkan.cpp282
1 files changed, 279 insertions, 3 deletions
diff --git a/tests/auto/gui/qvulkan/tst_qvulkan.cpp b/tests/auto/gui/qvulkan/tst_qvulkan.cpp
index 2803b84e8c..8027935003 100644
--- a/tests/auto/gui/qvulkan/tst_qvulkan.cpp
+++ b/tests/auto/gui/qvulkan/tst_qvulkan.cpp
@@ -28,7 +28,7 @@
#include <QtGui/QVulkanInstance>
#include <QtGui/QVulkanFunctions>
-#include <QtGui/QWindow>
+#include <QtGui/QVulkanWindow>
#include <QtTest/QtTest>
@@ -41,8 +41,11 @@ class tst_QVulkan : public QObject
private slots:
void vulkanInstance();
void vulkanCheckSupported();
- void vulkanWindow();
+ void vulkanPlainWindow();
void vulkanVersionRequest();
+ void vulkanWindow();
+ void vulkanWindowRenderer();
+ void vulkanWindowGrab();
};
void tst_QVulkan::vulkanInstance()
@@ -102,7 +105,7 @@ void tst_QVulkan::vulkanCheckSupported()
}
}
-void tst_QVulkan::vulkanWindow()
+void tst_QVulkan::vulkanPlainWindow()
{
QVulkanInstance inst;
if (!inst.create())
@@ -154,6 +157,279 @@ void tst_QVulkan::vulkanVersionRequest()
QCOMPARE(inst.errorCode(), VK_ERROR_INCOMPATIBLE_DRIVER);
}
+static void waitForUnexposed(QWindow *w)
+{
+ QElapsedTimer timer;
+ timer.start();
+ while (w->isExposed()) {
+ int remaining = 5000 - int(timer.elapsed());
+ if (remaining <= 0)
+ break;
+ QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
+ QCoreApplication::sendPostedEvents(Q_NULLPTR, QEvent::DeferredDelete);
+ QTest::qSleep(10);
+ }
+}
+
+void tst_QVulkan::vulkanWindow()
+{
+ QVulkanInstance inst;
+ if (!inst.create())
+ QSKIP("Vulkan init failed; skip");
+
+ // First let's forget to set the instance.
+ QVulkanWindow w;
+ QVERIFY(!w.isValid());
+ w.resize(1024, 768);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+ QVERIFY(!w.isValid());
+
+ // Now set it. A simple hide - show should be enough to correct, this, no
+ // need for a full destroy - create.
+ w.hide();
+ waitForUnexposed(&w);
+ w.setVulkanInstance(&inst);
+ QVector<VkPhysicalDeviceProperties> pdevs = w.availablePhysicalDevices();
+ if (pdevs.isEmpty())
+ QSKIP("No Vulkan physical devices; skip");
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+ QVERIFY(w.isValid());
+ QCOMPARE(w.vulkanInstance(), &inst);
+ QVulkanInfoVector<QVulkanExtension> exts = w.supportedDeviceExtensions();
+
+ // Now destroy and recreate.
+ w.destroy();
+ waitForUnexposed(&w);
+ QVERIFY(!w.isValid());
+ // check that flags can be set between a destroy() - show()
+ w.setFlags(QVulkanWindow::PersistentResources);
+ // supported lists can be queried before expose too
+ QVERIFY(w.supportedDeviceExtensions() == exts);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+ QVERIFY(w.isValid());
+ QVERIFY(w.flags().testFlag(QVulkanWindow::PersistentResources));
+
+ QVERIFY(w.physicalDevice() != VK_NULL_HANDLE);
+ QVERIFY(w.physicalDeviceProperties() != nullptr);
+ QVERIFY(w.device() != VK_NULL_HANDLE);
+ QVERIFY(w.graphicsQueue() != VK_NULL_HANDLE);
+ QVERIFY(w.graphicsCommandPool() != VK_NULL_HANDLE);
+ QVERIFY(w.defaultRenderPass() != VK_NULL_HANDLE);
+
+ QVERIFY(w.concurrentFrameCount() > 0);
+ QVERIFY(w.concurrentFrameCount() <= QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT);
+}
+
+class TestVulkanRenderer;
+
+class TestVulkanWindow : public QVulkanWindow
+{
+public:
+ QVulkanWindowRenderer *createRenderer() override;
+
+private:
+ TestVulkanRenderer *m_renderer = nullptr;
+};
+
+struct TestVulkan {
+ int preInitResCount = 0;
+ int initResCount = 0;
+ int initSwcResCount = 0;
+ int releaseResCount = 0;
+ int releaseSwcResCount = 0;
+ int startNextFrameCount = 0;
+} testVulkan;
+
+class TestVulkanRenderer : public QVulkanWindowRenderer
+{
+public:
+ TestVulkanRenderer(QVulkanWindow *w) : m_window(w) { }
+
+ void preInitResources() override;
+ void initResources() override;
+ void initSwapChainResources() override;
+ void releaseSwapChainResources() override;
+ void releaseResources() override;
+
+ void startNextFrame() override;
+
+private:
+ QVulkanWindow *m_window;
+ QVulkanDeviceFunctions *m_devFuncs;
+};
+
+void TestVulkanRenderer::preInitResources()
+{
+ if (testVulkan.initResCount) {
+ qWarning("initResources called before preInitResources?!");
+ testVulkan.preInitResCount = -1;
+ return;
+ }
+
+ // Ensure the physical device and the surface are available at this stage.
+ VkPhysicalDevice physDev = m_window->physicalDevice();
+ if (physDev == VK_NULL_HANDLE) {
+ qWarning("No physical device in preInitResources");
+ testVulkan.preInitResCount = -1;
+ return;
+ }
+ VkSurfaceKHR surface = m_window->vulkanInstance()->surfaceForWindow(m_window);
+ if (surface == VK_NULL_HANDLE) {
+ qWarning("No surface in preInitResources");
+ testVulkan.preInitResCount = -1;
+ return;
+ }
+
+ ++testVulkan.preInitResCount;
+}
+
+void TestVulkanRenderer::initResources()
+{
+ m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
+ ++testVulkan.initResCount;
+}
+
+void TestVulkanRenderer::initSwapChainResources()
+{
+ ++testVulkan.initSwcResCount;
+}
+
+void TestVulkanRenderer::releaseSwapChainResources()
+{
+ ++testVulkan.releaseSwcResCount;
+}
+
+void TestVulkanRenderer::releaseResources()
+{
+ ++testVulkan.releaseResCount;
+}
+
+void TestVulkanRenderer::startNextFrame()
+{
+ ++testVulkan.startNextFrameCount;
+
+ VkClearColorValue clearColor = { 0, 1, 0, 1 };
+ VkClearDepthStencilValue clearDS = { 1, 0 };
+ VkClearValue clearValues[2];
+ memset(clearValues, 0, sizeof(clearValues));
+ clearValues[0].color = clearColor;
+ clearValues[1].depthStencil = clearDS;
+
+ VkRenderPassBeginInfo rpBeginInfo;
+ memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
+ rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ rpBeginInfo.renderPass = m_window->defaultRenderPass();
+ rpBeginInfo.framebuffer = m_window->currentFramebuffer();
+ const QSize sz = m_window->swapChainImageSize();
+ rpBeginInfo.renderArea.extent.width = sz.width();
+ rpBeginInfo.renderArea.extent.height = sz.height();
+ rpBeginInfo.clearValueCount = 2;
+ rpBeginInfo.pClearValues = clearValues;
+ VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
+ m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ m_devFuncs->vkCmdEndRenderPass(cmdBuf);
+
+ m_window->frameReady();
+}
+
+QVulkanWindowRenderer *TestVulkanWindow::createRenderer()
+{
+ Q_ASSERT(!m_renderer);
+ m_renderer = new TestVulkanRenderer(this);
+ return m_renderer;
+}
+
+void tst_QVulkan::vulkanWindowRenderer()
+{
+ QVulkanInstance inst;
+ if (!inst.create())
+ QSKIP("Vulkan init failed; skip");
+
+ testVulkan = TestVulkan();
+
+ TestVulkanWindow w;
+ w.setVulkanInstance(&inst);
+ w.resize(1024, 768);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+
+ if (w.availablePhysicalDevices().isEmpty())
+ QSKIP("No Vulkan physical devices; skip");
+
+ QVERIFY(testVulkan.preInitResCount == 1);
+ QVERIFY(testVulkan.initResCount == 1);
+ QVERIFY(testVulkan.initSwcResCount == 1);
+ // this has to be QTRY due to the async update in QVulkanWindowPrivate::ensureStarted()
+ QTRY_VERIFY(testVulkan.startNextFrameCount >= 1);
+
+ QVERIFY(!w.swapChainImageSize().isEmpty());
+ QVERIFY(w.colorFormat() != VK_FORMAT_UNDEFINED);
+ QVERIFY(w.depthStencilFormat() != VK_FORMAT_UNDEFINED);
+
+ w.destroy();
+ waitForUnexposed(&w);
+ QVERIFY(testVulkan.releaseSwcResCount == 1);
+ QVERIFY(testVulkan.releaseResCount == 1);
+}
+
+void tst_QVulkan::vulkanWindowGrab()
+{
+ QVulkanInstance inst;
+ inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
+ if (!inst.create())
+ QSKIP("Vulkan init failed; skip");
+
+ testVulkan = TestVulkan();
+
+ TestVulkanWindow w;
+ w.setVulkanInstance(&inst);
+ w.resize(1024, 768);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+
+ if (w.availablePhysicalDevices().isEmpty())
+ QSKIP("No Vulkan physical devices; skip");
+
+ if (!w.supportsGrab())
+ QSKIP("No grab support; skip");
+
+ QVERIFY(!w.swapChainImageSize().isEmpty());
+
+ QImage img1 = w.grab();
+ QImage img2 = w.grab();
+ QImage img3 = w.grab();
+
+ QVERIFY(!img1.isNull());
+ QVERIFY(!img2.isNull());
+ QVERIFY(!img3.isNull());
+
+ QCOMPARE(img1.size(), w.swapChainImageSize());
+ QCOMPARE(img2.size(), w.swapChainImageSize());
+ QCOMPARE(img3.size(), w.swapChainImageSize());
+
+ QRgb a = img1.pixel(10, 20);
+ QRgb b = img2.pixel(5, 5);
+ QRgb c = img3.pixel(50, 30);
+
+ QCOMPARE(a, b);
+ QCOMPARE(b, c);
+ QRgb refPixel = qRgb(0, 255, 0);
+
+ int redFuzz = qAbs(qRed(a) - qRed(refPixel));
+ int greenFuzz = qAbs(qGreen(a) - qGreen(refPixel));
+ int blueFuzz = qAbs(qBlue(a) - qBlue(refPixel));
+
+ QVERIFY(redFuzz <= 1);
+ QVERIFY(blueFuzz <= 1);
+ QVERIFY(greenFuzz <= 1);
+
+ w.destroy();
+}
+
QTEST_MAIN(tst_QVulkan)
#include "tst_qvulkan.moc"