summaryrefslogtreecommitdiffstats
path: root/tests/manual/rhi/shared
diff options
context:
space:
mode:
Diffstat (limited to 'tests/manual/rhi/shared')
-rw-r--r--tests/manual/rhi/shared/OpenfootageNET_fieldairport-512.hdrbin0 -> 385303 bytes
-rw-r--r--tests/manual/rhi/shared/buildshaders.bat5
-rw-r--r--tests/manual/rhi/shared/bwqt224_64.pngbin0 -> 6339 bytes
-rw-r--r--tests/manual/rhi/shared/bwqt224_64_nomips.ddsbin0 -> 9712 bytes
-rw-r--r--tests/manual/rhi/shared/color.frag15
-rw-r--r--tests/manual/rhi/shared/color.frag.qsbbin0 -> 1483 bytes
-rw-r--r--tests/manual/rhi/shared/color.vert19
-rw-r--r--tests/manual/rhi/shared/color.vert.qsbbin0 -> 1648 bytes
-rw-r--r--tests/manual/rhi/shared/cube.h119
-rw-r--r--tests/manual/rhi/shared/dds_bc1.h137
-rw-r--r--tests/manual/rhi/shared/examplefw.h548
-rw-r--r--tests/manual/rhi/shared/qt256.pngbin0 -> 6208 bytes
-rw-r--r--tests/manual/rhi/shared/qt256_bc1_9mips.ddsbin0 -> 43832 bytes
-rw-r--r--tests/manual/rhi/shared/texture.frag18
-rw-r--r--tests/manual/rhi/shared/texture.frag.qsbbin0 -> 1710 bytes
-rw-r--r--tests/manual/rhi/shared/texture.vert21
-rw-r--r--tests/manual/rhi/shared/texture.vert.qsbbin0 -> 1897 bytes
-rw-r--r--tests/manual/rhi/shared/texture_ms4.frag20
-rw-r--r--tests/manual/rhi/shared/texture_ms4.frag.qsbbin0 -> 1847 bytes
19 files changed, 902 insertions, 0 deletions
diff --git a/tests/manual/rhi/shared/OpenfootageNET_fieldairport-512.hdr b/tests/manual/rhi/shared/OpenfootageNET_fieldairport-512.hdr
new file mode 100644
index 0000000000..abdebdb74b
--- /dev/null
+++ b/tests/manual/rhi/shared/OpenfootageNET_fieldairport-512.hdr
Binary files differ
diff --git a/tests/manual/rhi/shared/buildshaders.bat b/tests/manual/rhi/shared/buildshaders.bat
new file mode 100644
index 0000000000..dde5f7b3d2
--- /dev/null
+++ b/tests/manual/rhi/shared/buildshaders.bat
@@ -0,0 +1,5 @@
+qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c color.vert -o color.vert.qsb
+qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c color.frag -o color.frag.qsb
+qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c texture.vert -o texture.vert.qsb
+qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c texture.frag -o texture.frag.qsb
+qsb --hlsl 50 --msl 12 -c texture_ms4.frag -o texture_ms4.frag.qsb
diff --git a/tests/manual/rhi/shared/bwqt224_64.png b/tests/manual/rhi/shared/bwqt224_64.png
new file mode 100644
index 0000000000..8020cbb2a8
--- /dev/null
+++ b/tests/manual/rhi/shared/bwqt224_64.png
Binary files differ
diff --git a/tests/manual/rhi/shared/bwqt224_64_nomips.dds b/tests/manual/rhi/shared/bwqt224_64_nomips.dds
new file mode 100644
index 0000000000..393cfec9db
--- /dev/null
+++ b/tests/manual/rhi/shared/bwqt224_64_nomips.dds
Binary files differ
diff --git a/tests/manual/rhi/shared/color.frag b/tests/manual/rhi/shared/color.frag
new file mode 100644
index 0000000000..bb94a5a605
--- /dev/null
+++ b/tests/manual/rhi/shared/color.frag
@@ -0,0 +1,15 @@
+#version 440
+
+layout(location = 0) in vec3 v_color;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+void main()
+{
+ fragColor = vec4(v_color * ubuf.opacity, ubuf.opacity);
+}
diff --git a/tests/manual/rhi/shared/color.frag.qsb b/tests/manual/rhi/shared/color.frag.qsb
new file mode 100644
index 0000000000..3a965682eb
--- /dev/null
+++ b/tests/manual/rhi/shared/color.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/color.vert b/tests/manual/rhi/shared/color.vert
new file mode 100644
index 0000000000..43af543f44
--- /dev/null
+++ b/tests/manual/rhi/shared/color.vert
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+
+layout(location = 0) out vec3 v_color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_color = color;
+ gl_Position = ubuf.mvp * position;
+}
diff --git a/tests/manual/rhi/shared/color.vert.qsb b/tests/manual/rhi/shared/color.vert.qsb
new file mode 100644
index 0000000000..e34eae79a0
--- /dev/null
+++ b/tests/manual/rhi/shared/color.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/cube.h b/tests/manual/rhi/shared/cube.h
new file mode 100644
index 0000000000..252e80c56c
--- /dev/null
+++ b/tests/manual/rhi/shared/cube.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2015-2016 The Khronos Group Inc.
+ * Copyright (c) 2015-2016 Valve Corporation
+ * Copyright (c) 2015-2016 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Chia-I Wu <olv@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ * Author: Ian Elliott <ian@LunarG.com>
+ * Author: Ian Elliott <ianelliott@google.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Gwan-gyeong Mun <elongbug@gmail.com>
+ * Author: Tony Barbour <tony@LunarG.com>
+ * Author: Bill Hollings <bill.hollings@brenwill.com>
+ */
+
+#ifndef CUBE_H
+#define CUBE_H
+
+// clang-format off
+static const float cube[] = {
+ -1.0f,-1.0f,-1.0f, // -X side
+ -1.0f,-1.0f, 1.0f,
+ -1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f,-1.0f,
+ -1.0f,-1.0f,-1.0f,
+
+ -1.0f,-1.0f,-1.0f, // -Z side
+ 1.0f, 1.0f,-1.0f,
+ 1.0f,-1.0f,-1.0f,
+ -1.0f,-1.0f,-1.0f,
+ -1.0f, 1.0f,-1.0f,
+ 1.0f, 1.0f,-1.0f,
+
+ -1.0f,-1.0f,-1.0f, // -Y side
+ 1.0f,-1.0f,-1.0f,
+ 1.0f,-1.0f, 1.0f,
+ -1.0f,-1.0f,-1.0f,
+ 1.0f,-1.0f, 1.0f,
+ -1.0f,-1.0f, 1.0f,
+
+ -1.0f, 1.0f,-1.0f, // +Y side
+ -1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f,-1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f,-1.0f,
+
+ 1.0f, 1.0f,-1.0f, // +X side
+ 1.0f, 1.0f, 1.0f,
+ 1.0f,-1.0f, 1.0f,
+ 1.0f,-1.0f, 1.0f,
+ 1.0f,-1.0f,-1.0f,
+ 1.0f, 1.0f,-1.0f,
+
+ -1.0f, 1.0f, 1.0f, // +Z side
+ -1.0f,-1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ -1.0f,-1.0f, 1.0f,
+ 1.0f,-1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+
+ 0.0f, 1.0f, // -X side
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 0.0f,
+ 0.0f, 1.0f,
+
+ 1.0f, 1.0f, // -Z side
+ 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+ 0.0f, 0.0f,
+
+ 1.0f, 0.0f, // -Y side
+ 1.0f, 1.0f,
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 0.0f, 0.0f,
+
+ 1.0f, 0.0f, // +Y side
+ 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+
+ 1.0f, 0.0f, // +X side
+ 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+
+ 0.0f, 0.0f, // +Z side
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+};
+// clang-format on
+
+#endif
diff --git a/tests/manual/rhi/shared/dds_bc1.h b/tests/manual/rhi/shared/dds_bc1.h
new file mode 100644
index 0000000000..3c98a94cfc
--- /dev/null
+++ b/tests/manual/rhi/shared/dds_bc1.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+static const quint32 DDS_MAGIC = 0x20534444; // 'DDS '
+static const quint32 DDS_FOURCC = 4;
+
+#define FOURCC(c0, c1, c2, c3) ((c0) | ((c1) << 8) | ((c2) << 16) | ((c3 << 24)))
+
+struct DDS_PIXELFORMAT {
+ quint32 size;
+ quint32 flags;
+ quint32 fourCC;
+ quint32 rgbBitCount;
+ quint32 rBitMask;
+ quint32 gBitMask;
+ quint32 bBitMask;
+ quint32 aBitMask;
+};
+
+struct DDS_HEADER {
+ quint32 size;
+ quint32 flags;
+ quint32 height;
+ quint32 width;
+ quint32 pitch;
+ quint32 depth;
+ quint32 mipMapCount;
+ quint32 reserved1[11];
+ DDS_PIXELFORMAT pixelFormat;
+ quint32 caps;
+ quint32 caps2;
+ quint32 caps3;
+ quint32 caps4;
+ quint32 reserved2;
+};
+
+static quint32 bc1size(const QSize &size)
+{
+ static const quint32 blockSize = 8; // 8 bytes for BC1
+ const quint32 bytesPerLine = qMax<quint32>(1, (size.width() + 3) / 4) * blockSize;
+ const quint32 ySize = qMax<quint32>(1, (size.height() + 3) / 4);
+ return bytesPerLine * ySize;
+}
+
+static QByteArrayList loadBC1(const QString &filename, QSize *size)
+{
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("Failed to open %s", qPrintable(filename));
+ return QByteArrayList();
+ }
+
+ quint32 magic = 0;
+ f.read(reinterpret_cast<char *>(&magic), sizeof(magic));
+ if (magic != DDS_MAGIC) {
+ qWarning("%s is not a DDS file", qPrintable(filename));
+ return QByteArrayList();
+ }
+ DDS_HEADER header;
+ f.read(reinterpret_cast<char *>(&header), sizeof(header));
+ if (header.size != sizeof(DDS_HEADER)) {
+ qWarning("Invalid DDS header size");
+ return QByteArrayList();
+ }
+ if (header.pixelFormat.size != sizeof(DDS_PIXELFORMAT)) {
+ qWarning("Invalid DDS pixel format size");
+ return QByteArrayList();
+ }
+ if (!(header.pixelFormat.flags & DDS_FOURCC)) {
+ qWarning("Invalid DDS pixel format");
+ return QByteArrayList();
+ }
+ if (header.pixelFormat.fourCC != FOURCC('D', 'X', 'T', '1')) {
+ qWarning("Only DXT1 (BC1) is supported");
+ return QByteArrayList();
+ }
+
+ QByteArrayList data;
+ QSize sz(header.width, header.height);
+ for (quint32 level = 0; level < header.mipMapCount; ++level) {
+ data.append(f.read(bc1size(sz)));
+ sz.setWidth(qMax(1, sz.width() / 2));
+ sz.setHeight(qMax(1, sz.height() / 2));
+ }
+
+ if (size)
+ *size = QSize(header.width, header.height);
+
+ return data;
+}
diff --git a/tests/manual/rhi/shared/examplefw.h b/tests/manual/rhi/shared/examplefw.h
new file mode 100644
index 0000000000..450aa172c2
--- /dev/null
+++ b/tests/manual/rhi/shared/examplefw.h
@@ -0,0 +1,548 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// Adapted from hellominimalcrossgfxtriangle with the frame rendering stripped out.
+// Include this file and implement Window::customInit, release and render.
+// Debug/validation layer is enabled for D3D and Vulkan.
+
+#include <QGuiApplication>
+#include <QCommandLineParser>
+#include <QWindow>
+#include <QPlatformSurfaceEvent>
+#include <QElapsedTimer>
+#include <QTimer>
+
+#include <QtGui/private/qshader_p.h>
+#include <QFile>
+#include <QtGui/private/qrhiprofiler_p.h>
+#include <QtGui/private/qrhinull_p.h>
+
+#ifndef QT_NO_OPENGL
+#include <QtGui/private/qrhigles2_p.h>
+#include <QOffscreenSurface>
+#endif
+
+#if QT_CONFIG(vulkan)
+#include <QLoggingCategory>
+#include <QtGui/private/qrhivulkan_p.h>
+#endif
+
+#ifdef Q_OS_WIN
+#include <QtGui/private/qrhid3d11_p.h>
+#endif
+
+#ifdef Q_OS_DARWIN
+#include <QtGui/private/qrhimetal_p.h>
+#endif
+
+QShader getShader(const QString &name)
+{
+ QFile f(name);
+ if (f.open(QIODevice::ReadOnly))
+ return QShader::fromSerialized(f.readAll());
+
+ return QShader();
+}
+
+enum GraphicsApi
+{
+ Null,
+ OpenGL,
+ Vulkan,
+ D3D11,
+ Metal
+};
+
+GraphicsApi graphicsApi;
+
+QString graphicsApiName()
+{
+ switch (graphicsApi) {
+ case Null:
+ return QLatin1String("Null (no output)");
+ case OpenGL:
+ return QLatin1String("OpenGL 2.x");
+ case Vulkan:
+ return QLatin1String("Vulkan");
+ case D3D11:
+ return QLatin1String("Direct3D 11");
+ case Metal:
+ return QLatin1String("Metal");
+ default:
+ break;
+ }
+ return QString();
+}
+
+QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
+int sampleCount = 1;
+QRhiSwapChain::Flags scFlags = 0;
+QRhi::EndFrameFlags endFrameFlags = 0;
+
+class Window : public QWindow
+{
+public:
+ Window();
+ ~Window();
+
+protected:
+ void init();
+ void releaseResources();
+ void resizeSwapChain();
+ void releaseSwapChain();
+ void render();
+
+ void customInit();
+ void customRelease();
+ void customRender();
+
+ void exposeEvent(QExposeEvent *) override;
+ bool event(QEvent *) override;
+
+ bool m_running = false;
+ bool m_notExposed = false;
+ bool m_newlyExposed = false;
+
+ QRhi *m_r = nullptr;
+ bool m_hasSwapChain = false;
+ QRhiSwapChain *m_sc = nullptr;
+ QRhiRenderBuffer *m_ds = nullptr;
+ QRhiRenderPassDescriptor *m_rp = nullptr;
+
+ QMatrix4x4 m_proj;
+
+ QElapsedTimer m_timer;
+ int m_frameCount;
+
+#ifndef QT_NO_OPENGL
+ QOffscreenSurface *m_fallbackSurface = nullptr;
+#endif
+
+ friend int main(int, char**);
+};
+
+Window::Window()
+{
+ // Tell the platform plugin what we want.
+ switch (graphicsApi) {
+ case OpenGL:
+#if QT_CONFIG(opengl)
+ setSurfaceType(OpenGLSurface);
+ setFormat(QRhiGles2InitParams::adjustedFormat());
+#endif
+ break;
+ case Vulkan:
+ setSurfaceType(VulkanSurface);
+ break;
+ case D3D11:
+ setSurfaceType(OpenGLSurface); // not a typo
+ break;
+ case Metal:
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+ setSurfaceType(MetalSurface);
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+Window::~Window()
+{
+ releaseResources();
+}
+
+void Window::exposeEvent(QExposeEvent *)
+{
+ // initialize and start rendering when the window becomes usable for graphics purposes
+ if (isExposed() && !m_running) {
+ m_running = true;
+ init();
+ resizeSwapChain();
+ }
+
+ const QSize surfaceSize = m_hasSwapChain ? m_sc->surfacePixelSize() : QSize();
+
+ // stop pushing frames when not exposed (or size is 0)
+ if ((!isExposed() || (m_hasSwapChain && surfaceSize.isEmpty())) && m_running)
+ m_notExposed = true;
+
+ // continue when exposed again and the surface has a valid size.
+ // note that the surface size can be (0, 0) even though size() reports a valid one...
+ if (isExposed() && m_running && m_notExposed && !surfaceSize.isEmpty()) {
+ m_notExposed = false;
+ m_newlyExposed = true;
+ }
+
+ // always render a frame on exposeEvent() (when exposed) in order to update
+ // immediately on window resize.
+ if (isExposed() && !surfaceSize.isEmpty())
+ render();
+}
+
+bool Window::event(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::UpdateRequest:
+ render();
+ break;
+
+ case QEvent::PlatformSurface:
+ // this is the proper time to tear down the swapchain (while the native window and surface are still around)
+ if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
+ releaseSwapChain();
+ break;
+
+ default:
+ break;
+ }
+
+ return QWindow::event(e);
+}
+
+void Window::init()
+{
+ if (graphicsApi == Null) {
+ QRhiNullInitParams params;
+ m_r = QRhi::create(QRhi::Null, &params, rhiFlags);
+ }
+
+#ifndef QT_NO_OPENGL
+ if (graphicsApi == OpenGL) {
+ m_fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+ QRhiGles2InitParams params;
+ params.fallbackSurface = m_fallbackSurface;
+ params.window = this;
+ m_r = QRhi::create(QRhi::OpenGLES2, &params, rhiFlags);
+ }
+#endif
+
+#if QT_CONFIG(vulkan)
+ if (graphicsApi == Vulkan) {
+ QRhiVulkanInitParams params;
+ params.inst = vulkanInstance();
+ params.window = this;
+ m_r = QRhi::create(QRhi::Vulkan, &params, rhiFlags);
+ }
+#endif
+
+#ifdef Q_OS_WIN
+ if (graphicsApi == D3D11) {
+ QRhiD3D11InitParams params;
+ params.enableDebugLayer = true;
+ m_r = QRhi::create(QRhi::D3D11, &params, rhiFlags);
+ }
+#endif
+
+#ifdef Q_OS_DARWIN
+ if (graphicsApi == Metal) {
+ QRhiMetalInitParams params;
+ m_r = QRhi::create(QRhi::Metal, &params, rhiFlags);
+ }
+#endif
+
+ if (!m_r)
+ qFatal("Failed to create RHI backend");
+
+ // now onto the backend-independent init
+
+ m_sc = m_r->newSwapChain();
+ // allow depth-stencil, although we do not actually enable depth test/write for the triangle
+ m_ds = m_r->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(), // no need to set the size yet
+ sampleCount,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ m_sc->setWindow(this);
+ m_sc->setDepthStencil(m_ds);
+ m_sc->setSampleCount(sampleCount);
+ m_sc->setFlags(scFlags);
+ m_rp = m_sc->newCompatibleRenderPassDescriptor();
+ m_sc->setRenderPassDescriptor(m_rp);
+
+ customInit();
+}
+
+void Window::releaseResources()
+{
+ customRelease();
+
+ delete m_rp;
+ m_rp = nullptr;
+
+ delete m_ds;
+ m_ds = nullptr;
+
+ delete m_sc;
+ m_sc = nullptr;
+
+ delete m_r;
+ m_r = nullptr;
+
+#ifndef QT_NO_OPENGL
+ delete m_fallbackSurface;
+ m_fallbackSurface = nullptr;
+#endif
+}
+
+void Window::resizeSwapChain()
+{
+ const QSize outputSize = m_sc->surfacePixelSize();
+
+ m_ds->setPixelSize(outputSize);
+ m_ds->build(); // == m_ds->release(); m_ds->build();
+
+ m_hasSwapChain = m_sc->buildOrResize();
+
+ m_frameCount = 0;
+ m_timer.restart();
+
+ m_proj = m_r->clipSpaceCorrMatrix();
+ m_proj.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
+ m_proj.translate(0, 0, -4);
+}
+
+void Window::releaseSwapChain()
+{
+ if (m_hasSwapChain) {
+ m_hasSwapChain = false;
+ m_sc->release();
+ }
+}
+
+void Window::render()
+{
+ if (!m_hasSwapChain || m_notExposed)
+ return;
+
+ // If the window got resized or got newly exposed, resize the swapchain.
+ // (the newly-exposed case is not actually required by some
+ // platforms/backends, but f.ex. Vulkan on Windows seems to need it)
+ if (m_sc->currentPixelSize() != m_sc->surfacePixelSize() || m_newlyExposed) {
+ resizeSwapChain();
+ if (!m_hasSwapChain)
+ return;
+ m_newlyExposed = false;
+ }
+
+ // Start a new frame. This is where we block when too far ahead of
+ // GPU/present, and that's what throttles the thread to the refresh rate.
+ // (except for OpenGL where it happens either in endFrame or somewhere else
+ // depending on the GL implementation)
+ QRhi::FrameOpResult r = m_r->beginFrame(m_sc);
+ if (r == QRhi::FrameOpSwapChainOutOfDate) {
+ resizeSwapChain();
+ if (!m_hasSwapChain)
+ return;
+ r = m_r->beginFrame(m_sc);
+ }
+ if (r != QRhi::FrameOpSuccess) {
+ requestUpdate();
+ return;
+ }
+
+ m_frameCount += 1;
+ if (m_timer.elapsed() > 1000) {
+ if (rhiFlags.testFlag(QRhi::EnableProfiling)) {
+ const QRhiProfiler::CpuTime ff = m_r->profiler()->frameToFrameTimes(m_sc);
+ const QRhiProfiler::CpuTime be = m_r->profiler()->frameBuildTimes(m_sc);
+ const QRhiProfiler::GpuTime gp = m_r->profiler()->gpuFrameTimes(m_sc);
+ if (m_r->isFeatureSupported(QRhi::Timestamps)) {
+ qDebug("ca. %d fps. "
+ "frame-to-frame: min %lld max %lld avg %f. "
+ "frame build: min %lld max %lld avg %f. "
+ "gpu frame time: min %f max %f avg %f",
+ m_frameCount,
+ ff.minTime, ff.maxTime, ff.avgTime,
+ be.minTime, be.maxTime, be.avgTime,
+ gp.minTime, gp.maxTime, gp.avgTime);
+ } else {
+ qDebug("ca. %d fps. "
+ "frame-to-frame: min %lld max %lld avg %f. "
+ "frame build: min %lld max %lld avg %f. ",
+ m_frameCount,
+ ff.minTime, ff.maxTime, ff.avgTime,
+ be.minTime, be.maxTime, be.avgTime);
+ }
+ } else {
+ qDebug("ca. %d fps", m_frameCount);
+ }
+
+ m_timer.restart();
+ m_frameCount = 0;
+ }
+
+ customRender();
+
+ m_r->endFrame(m_sc, endFrameFlags);
+
+ if (!scFlags.testFlag(QRhiSwapChain::NoVSync))
+ requestUpdate();
+ else // try prevent all delays when NoVSync
+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QGuiApplication app(argc, argv);
+
+ // Defaults.
+#if defined(Q_OS_WIN)
+ graphicsApi = D3D11;
+#elif defined(Q_OS_DARWIN)
+ graphicsApi = Metal;
+#elif QT_CONFIG(vulkan)
+ graphicsApi = Vulkan;
+#else
+ graphicsApi = OpenGL;
+#endif
+
+ // Allow overriding via the command line.
+ QCommandLineParser cmdLineParser;
+ cmdLineParser.addHelpOption();
+ QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null"));
+ cmdLineParser.addOption(nullOption);
+ QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)"));
+ cmdLineParser.addOption(glOption);
+ QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
+ cmdLineParser.addOption(vkOption);
+ QCommandLineOption d3dOption({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
+ cmdLineParser.addOption(d3dOption);
+ QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
+ cmdLineParser.addOption(mtlOption);
+ // Testing cleanup both with QWindow::close() (hitting X or Alt-F4) and
+ // QCoreApplication::quit() (e.g. what a menu widget would do) is important.
+ // Use this parameter for the latter.
+ QCommandLineOption sdOption({ "s", "self-destruct" }, QLatin1String("Self destruct after 5 seconds"));
+ cmdLineParser.addOption(sdOption);
+
+ cmdLineParser.process(app);
+ if (cmdLineParser.isSet(nullOption))
+ graphicsApi = Null;
+ if (cmdLineParser.isSet(glOption))
+ graphicsApi = OpenGL;
+ if (cmdLineParser.isSet(vkOption))
+ graphicsApi = Vulkan;
+ if (cmdLineParser.isSet(d3dOption))
+ graphicsApi = D3D11;
+ if (cmdLineParser.isSet(mtlOption))
+ graphicsApi = Metal;
+
+ qDebug("Selected graphics API is %s", qPrintable(graphicsApiName()));
+ qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText()));
+
+#ifdef EXAMPLEFW_PREINIT
+ void preInit();
+ preInit();
+#endif
+
+ // OpenGL specifics.
+ QSurfaceFormat fmt;
+ fmt.setDepthBufferSize(24);
+ fmt.setStencilBufferSize(8);
+ if (sampleCount > 1)
+ fmt.setSamples(sampleCount);
+ if (scFlags.testFlag(QRhiSwapChain::NoVSync))
+ fmt.setSwapInterval(0);
+ if (scFlags.testFlag(QRhiSwapChain::sRGB))
+ fmt.setColorSpace(QSurfaceFormat::sRGBColorSpace);
+ QSurfaceFormat::setDefaultFormat(fmt);
+
+ // Vulkan setup.
+#if QT_CONFIG(vulkan)
+ QVulkanInstance inst;
+ if (graphicsApi == Vulkan) {
+#ifndef Q_OS_ANDROID
+ inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
+#else
+ inst.setLayers(QByteArrayList()
+ << "VK_LAYER_GOOGLE_threading"
+ << "VK_LAYER_LUNARG_parameter_validation"
+ << "VK_LAYER_LUNARG_object_tracker"
+ << "VK_LAYER_LUNARG_core_validation"
+ << "VK_LAYER_LUNARG_image"
+ << "VK_LAYER_LUNARG_swapchain"
+ << "VK_LAYER_GOOGLE_unique_objects");
+#endif
+ inst.setExtensions(QByteArrayList()
+ << "VK_KHR_get_physical_device_properties2");
+ if (!inst.create()) {
+ qWarning("Failed to create Vulkan instance, switching to OpenGL");
+ graphicsApi = OpenGL;
+ }
+ }
+#endif
+
+ // Create and show the window.
+ Window w;
+#if QT_CONFIG(vulkan)
+ if (graphicsApi == Vulkan)
+ w.setVulkanInstance(&inst);
+#endif
+ w.resize(1280, 720);
+ w.setTitle(QCoreApplication::applicationName() + QLatin1String(" - ") + graphicsApiName());
+ w.show();
+
+ if (cmdLineParser.isSet(sdOption))
+ QTimer::singleShot(5000, qGuiApp, SLOT(quit()));
+
+ int ret = app.exec();
+
+ // Window::event() will not get invoked when the
+ // PlatformSurfaceAboutToBeDestroyed event is sent during the QWindow
+ // destruction. That happens only when exiting via app::quit() instead of
+ // the more common QWindow::close(). Take care of it: if the
+ // QPlatformWindow is still around (there was no close() yet), get rid of
+ // the swapchain while it's not too late.
+ if (w.handle())
+ w.releaseSwapChain();
+
+ return ret;
+}
diff --git a/tests/manual/rhi/shared/qt256.png b/tests/manual/rhi/shared/qt256.png
new file mode 100644
index 0000000000..30c621c9c6
--- /dev/null
+++ b/tests/manual/rhi/shared/qt256.png
Binary files differ
diff --git a/tests/manual/rhi/shared/qt256_bc1_9mips.dds b/tests/manual/rhi/shared/qt256_bc1_9mips.dds
new file mode 100644
index 0000000000..734a5d184f
--- /dev/null
+++ b/tests/manual/rhi/shared/qt256_bc1_9mips.dds
Binary files differ
diff --git a/tests/manual/rhi/shared/texture.frag b/tests/manual/rhi/shared/texture.frag
new file mode 100644
index 0000000000..5405444c68
--- /dev/null
+++ b/tests/manual/rhi/shared/texture.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+} ubuf;
+
+layout(binding = 1) uniform sampler2D tex;
+
+void main()
+{
+ vec4 c = texture(tex, v_texcoord);
+ fragColor = vec4(c.rgb * c.a, c.a);
+}
diff --git a/tests/manual/rhi/shared/texture.frag.qsb b/tests/manual/rhi/shared/texture.frag.qsb
new file mode 100644
index 0000000000..31bcd7105e
--- /dev/null
+++ b/tests/manual/rhi/shared/texture.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/texture.vert b/tests/manual/rhi/shared/texture.vert
new file mode 100644
index 0000000000..ad7e783dfb
--- /dev/null
+++ b/tests/manual/rhi/shared/texture.vert
@@ -0,0 +1,21 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+
+layout(location = 0) out vec2 v_texcoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_texcoord = vec2(texcoord.x, texcoord.y);
+ if (ubuf.flip != 0)
+ v_texcoord.y = 1.0 - v_texcoord.y;
+ gl_Position = ubuf.mvp * position;
+}
diff --git a/tests/manual/rhi/shared/texture.vert.qsb b/tests/manual/rhi/shared/texture.vert.qsb
new file mode 100644
index 0000000000..1b9e52890d
--- /dev/null
+++ b/tests/manual/rhi/shared/texture.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/texture_ms4.frag b/tests/manual/rhi/shared/texture_ms4.frag
new file mode 100644
index 0000000000..d4f2bd3654
--- /dev/null
+++ b/tests/manual/rhi/shared/texture_ms4.frag
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+} ubuf;
+
+layout(binding = 1) uniform sampler2DMS tex;
+
+void main()
+{
+ ivec2 tc = ivec2(floor(vec2(textureSize(tex)) * v_texcoord));
+ vec4 c = texelFetch(tex, tc, 0) + texelFetch(tex, tc, 1) + texelFetch(tex, tc, 2) + texelFetch(tex, tc, 3);
+ c /= 4.0;
+ fragColor = vec4(c.rgb * c.a, c.a);
+}
diff --git a/tests/manual/rhi/shared/texture_ms4.frag.qsb b/tests/manual/rhi/shared/texture_ms4.frag.qsb
new file mode 100644
index 0000000000..7f187ce7f0
--- /dev/null
+++ b/tests/manual/rhi/shared/texture_ms4.frag.qsb
Binary files differ