summaryrefslogtreecommitdiffstats
path: root/examples/widgets/rhi/cuberhiwidget
diff options
context:
space:
mode:
Diffstat (limited to 'examples/widgets/rhi/cuberhiwidget')
-rw-r--r--examples/widgets/rhi/cuberhiwidget/CMakeLists.txt48
-rw-r--r--examples/widgets/rhi/cuberhiwidget/cube.h139
-rw-r--r--examples/widgets/rhi/cuberhiwidget/cuberhiwidget.pro12
-rw-r--r--examples/widgets/rhi/cuberhiwidget/cuberhiwidget.qrc6
-rw-r--r--examples/widgets/rhi/cuberhiwidget/examplewidget.cpp172
-rw-r--r--examples/widgets/rhi/cuberhiwidget/examplewidget.h72
-rw-r--r--examples/widgets/rhi/cuberhiwidget/main.cpp166
-rw-r--r--examples/widgets/rhi/cuberhiwidget/shader_assets/texture.frag.qsbbin0 -> 1042 bytes
-rw-r--r--examples/widgets/rhi/cuberhiwidget/shader_assets/texture.vert.qsbbin0 -> 1161 bytes
-rw-r--r--examples/widgets/rhi/cuberhiwidget/shaders/texture.frag12
-rw-r--r--examples/widgets/rhi/cuberhiwidget/shaders/texture.vert15
11 files changed, 642 insertions, 0 deletions
diff --git a/examples/widgets/rhi/cuberhiwidget/CMakeLists.txt b/examples/widgets/rhi/cuberhiwidget/CMakeLists.txt
new file mode 100644
index 0000000000..effa9a5a73
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/CMakeLists.txt
@@ -0,0 +1,48 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(cuberhiwidget LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(cuberhiwidget
+ examplewidget.cpp examplewidget.h cube.h
+ main.cpp
+)
+
+set_target_properties(cuberhiwidget PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+# needs GuiPrivate to be able to include <rhi/qrhi.h>
+target_link_libraries(cuberhiwidget PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::GuiPrivate
+ Qt6::Widgets
+)
+
+qt_add_resources(cuberhiwidget "cuberhiwidget"
+ PREFIX
+ "/"
+ FILES
+ "shader_assets/texture.vert.qsb"
+ "shader_assets/texture.frag.qsb"
+)
+
+install(TARGETS cuberhiwidget
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_app_script(
+ TARGET cuberhiwidget
+ OUTPUT_SCRIPT deploy_script
+ NO_UNSUPPORTED_PLATFORM_ERROR
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/widgets/rhi/cuberhiwidget/cube.h b/examples/widgets/rhi/cuberhiwidget/cube.h
new file mode 100644
index 0000000000..9d55eede92
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/cube.h
@@ -0,0 +1,139 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef CUBE_H
+#define CUBE_H
+
+// clang-format off
+static const float cube[] = {
+ -1.0f, -1.0f, -1.0f, // -X
+ -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
+ 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
+ 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
+ -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
+ 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
+ -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,
+
+ // UVs
+ 0.0f, 1.0f, // -X
+ 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
+ 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
+ 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
+ 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
+ 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
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+
+ // normals
+ -1.0, 0.0, 0.0, // -X
+ -1.0, 0.0, 0.0,
+ -1.0, 0.0, 0.0,
+ -1.0, 0.0, 0.0,
+ -1.0, 0.0, 0.0,
+ -1.0, 0.0, 0.0,
+
+ 0.0, 0.0, -1.0, // -Z
+ 0.0, 0.0, -1.0,
+ 0.0, 0.0, -1.0,
+ 0.0, 0.0, -1.0,
+ 0.0, 0.0, -1.0,
+ 0.0, 0.0, -1.0,
+
+ 0.0, -1.0, 0.0, // -Y
+ 0.0, -1.0, 0.0,
+ 0.0, -1.0, 0.0,
+ 0.0, -1.0, 0.0,
+ 0.0, -1.0, 0.0,
+ 0.0, -1.0, 0.0,
+
+ 0.0, 1.0, 0.0, // +Y
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+
+ 1.0, 0.0, 0.0, // +X
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+
+ 0.0, 0.0, 1.0, // +Z
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0
+};
+// clang-format on
+
+#endif
diff --git a/examples/widgets/rhi/cuberhiwidget/cuberhiwidget.pro b/examples/widgets/rhi/cuberhiwidget/cuberhiwidget.pro
new file mode 100644
index 0000000000..94abd29e08
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/cuberhiwidget.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+# needs gui-private to be able to include <rhi/qrhi.h>
+QT += gui-private widgets
+
+HEADERS += examplewidget.h
+SOURCES += examplewidget.cpp main.cpp
+
+RESOURCES += cuberhiwidget.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/rhi/cuberhiwidget
+INSTALLS += target
diff --git a/examples/widgets/rhi/cuberhiwidget/cuberhiwidget.qrc b/examples/widgets/rhi/cuberhiwidget/cuberhiwidget.qrc
new file mode 100644
index 0000000000..33ca81dbde
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/cuberhiwidget.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+ <file>shader_assets/texture.vert.qsb</file>
+ <file>shader_assets/texture.frag.qsb</file>
+</qresource>
+</RCC>
diff --git a/examples/widgets/rhi/cuberhiwidget/examplewidget.cpp b/examples/widgets/rhi/cuberhiwidget/examplewidget.cpp
new file mode 100644
index 0000000000..fe39d904dd
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/examplewidget.cpp
@@ -0,0 +1,172 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "examplewidget.h"
+#include "cube.h"
+#include <QFile>
+#include <QPainter>
+
+static const QSize CUBE_TEX_SIZE(512, 512);
+
+ExampleRhiWidget::ExampleRhiWidget(QWidget *parent)
+ : QRhiWidget(parent)
+{
+}
+
+//![init-1]
+void ExampleRhiWidget::initialize(QRhiCommandBuffer *)
+{
+ if (m_rhi != rhi()) {
+ m_rhi = rhi();
+ scene = {};
+ emit rhiChanged(QString::fromUtf8(m_rhi->backendName()));
+ }
+ if (m_pixelSize != renderTarget()->pixelSize()) {
+ m_pixelSize = renderTarget()->pixelSize();
+ emit resized();
+ }
+ if (m_sampleCount != renderTarget()->sampleCount()) {
+ m_sampleCount = renderTarget()->sampleCount();
+ scene = {};
+ }
+//![init-1]
+
+//![init-2]
+ if (!scene.vbuf) {
+ initScene();
+ updateCubeTexture();
+ }
+
+ scene.mvp = m_rhi->clipSpaceCorrMatrix();
+ scene.mvp.perspective(45.0f, m_pixelSize.width() / (float) m_pixelSize.height(), 0.01f, 1000.0f);
+ scene.mvp.translate(0, 0, -4);
+ updateMvp();
+}
+//![init-2]
+
+//![rotation-update]
+void ExampleRhiWidget::updateMvp()
+{
+ QMatrix4x4 mvp = scene.mvp * QMatrix4x4(QQuaternion::fromEulerAngles(QVector3D(30, itemData.cubeRotation, 0)).toRotationMatrix());
+ if (!scene.resourceUpdates)
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.get(), 0, 64, mvp.constData());
+}
+//![rotation-update]
+
+//![texture-update]
+void ExampleRhiWidget::updateCubeTexture()
+{
+ QImage image(CUBE_TEX_SIZE, QImage::Format_RGBA8888);
+ const QRect r(QPoint(0, 0), CUBE_TEX_SIZE);
+ QPainter p(&image);
+ p.fillRect(r, QGradient::DeepBlue);
+ QFont font;
+ font.setPointSize(24);
+ p.setFont(font);
+ p.drawText(r, itemData.cubeText);
+ p.end();
+
+ if (!scene.resourceUpdates)
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ scene.resourceUpdates->uploadTexture(scene.cubeTex.get(), image);
+}
+//![texture-update]
+
+static QShader getShader(const QString &name)
+{
+ QFile f(name);
+ return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
+}
+
+void ExampleRhiWidget::initScene()
+{
+//![setup-scene]
+ scene.vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube)));
+ scene.vbuf->create();
+
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ scene.resourceUpdates->uploadStaticBuffer(scene.vbuf.get(), cube);
+
+ scene.ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64));
+ scene.ubuf->create();
+
+ scene.cubeTex.reset(m_rhi->newTexture(QRhiTexture::RGBA8, CUBE_TEX_SIZE));
+ scene.cubeTex->create();
+
+ scene.sampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
+ scene.sampler->create();
+
+ scene.srb.reset(m_rhi->newShaderResourceBindings());
+ scene.srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, scene.ubuf.get()),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.cubeTex.get(), scene.sampler.get())
+ });
+ scene.srb->create();
+
+ scene.ps.reset(m_rhi->newGraphicsPipeline());
+ scene.ps->setDepthTest(true);
+ scene.ps->setDepthWrite(true);
+ scene.ps->setCullMode(QRhiGraphicsPipeline::Back);
+ scene.ps->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/shader_assets/texture.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/shader_assets/texture.frag.qsb")) }
+ });
+ QRhiVertexInputLayout inputLayout;
+ // The cube is provided as non-interleaved sets of positions, UVs, normals.
+ // Normals are not interesting here, only need the positions and UVs.
+ inputLayout.setBindings({
+ { 3 * sizeof(float) },
+ { 2 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
+ { 1, 1, QRhiVertexInputAttribute::Float2, 0 }
+ });
+ scene.ps->setSampleCount(m_sampleCount);
+ scene.ps->setVertexInputLayout(inputLayout);
+ scene.ps->setShaderResourceBindings(scene.srb.get());
+ scene.ps->setRenderPassDescriptor(renderTarget()->renderPassDescriptor());
+ scene.ps->create();
+//![setup-scene]
+}
+
+//![render]
+void ExampleRhiWidget::render(QRhiCommandBuffer *cb)
+{
+ if (itemData.cubeRotationDirty) {
+ itemData.cubeRotationDirty = false;
+ updateMvp();
+ }
+
+ if (itemData.cubeTextDirty) {
+ itemData.cubeTextDirty = false;
+ updateCubeTexture();
+ }
+
+ QRhiResourceUpdateBatch *resourceUpdates = scene.resourceUpdates;
+ if (resourceUpdates)
+ scene.resourceUpdates = nullptr;
+
+ const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f);
+ cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, resourceUpdates);
+
+ cb->setGraphicsPipeline(scene.ps.get());
+ cb->setViewport(QRhiViewport(0, 0, m_pixelSize.width(), m_pixelSize.height()));
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { scene.vbuf.get(), 0 },
+ { scene.vbuf.get(), quint32(36 * 3 * sizeof(float)) }
+ };
+ cb->setVertexInput(0, 2, vbufBindings);
+ cb->draw(36);
+
+ cb->endPass();
+}
+//![render]
+
+void ExampleRhiWidget::releaseResources()
+{
+ scene = {}; // a subsequent initialize() will recreate everything
+}
diff --git a/examples/widgets/rhi/cuberhiwidget/examplewidget.h b/examples/widgets/rhi/cuberhiwidget/examplewidget.h
new file mode 100644
index 0000000000..9cc554b3fb
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/examplewidget.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef EXAMPLEWIDGET_H
+#define EXAMPLEWIDGET_H
+
+#include <QRhiWidget>
+#include <rhi/qrhi.h>
+
+class ExampleRhiWidget : public QRhiWidget
+{
+ Q_OBJECT
+
+public:
+ ExampleRhiWidget(QWidget *parent = nullptr);
+
+ void initialize(QRhiCommandBuffer *cb) override;
+ void render(QRhiCommandBuffer *cb) override;
+ void releaseResources() override;
+//![data-setters]
+ void setCubeTextureText(const QString &s)
+ {
+ if (itemData.cubeText == s)
+ return;
+ itemData.cubeText = s;
+ itemData.cubeTextDirty = true;
+ update();
+ }
+
+ void setCubeRotation(float r)
+ {
+ if (itemData.cubeRotation == r)
+ return;
+ itemData.cubeRotation = r;
+ itemData.cubeRotationDirty = true;
+ update();
+ }
+//![data-setters]
+
+signals:
+ void resized();
+ void rhiChanged(const QString &apiName);
+
+private:
+ QRhi *m_rhi = nullptr;
+ int m_sampleCount = 1;
+ QSize m_pixelSize;
+
+ struct {
+ QRhiResourceUpdateBatch *resourceUpdates = nullptr;
+ std::unique_ptr<QRhiBuffer> vbuf;
+ std::unique_ptr<QRhiBuffer> ubuf;
+ std::unique_ptr<QRhiShaderResourceBindings> srb;
+ std::unique_ptr<QRhiGraphicsPipeline> ps;
+ std::unique_ptr<QRhiSampler> sampler;
+ std::unique_ptr<QRhiTexture> cubeTex;
+ QMatrix4x4 mvp;
+ } scene;
+
+ struct {
+ QString cubeText;
+ bool cubeTextDirty = false;
+ float cubeRotation = 0.0f;
+ bool cubeRotationDirty = false;
+ } itemData;
+
+ void initScene();
+ void updateMvp();
+ void updateCubeTexture();
+};
+
+#endif
diff --git a/examples/widgets/rhi/cuberhiwidget/main.cpp b/examples/widgets/rhi/cuberhiwidget/main.cpp
new file mode 100644
index 0000000000..95a29feee5
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/main.cpp
@@ -0,0 +1,166 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QSlider>
+#include <QTextEdit>
+#include <QPushButton>
+#include <QLabel>
+#include <QCheckBox>
+#include <QFileDialog>
+#include <QFontInfo>
+#include <QMouseEvent>
+#include <QDrag>
+#include <QMimeData>
+#include "examplewidget.h"
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ QVBoxLayout *layout = new QVBoxLayout;
+
+ ExampleRhiWidget *rhiWidget = new ExampleRhiWidget;
+ QLabel *overlayLabel = new QLabel(rhiWidget);
+ overlayLabel->setText(QObject::tr("This is a\nsemi-transparent\n overlay widget\n"
+ "placed on top of\nthe QRhiWidget."));
+ overlayLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
+ overlayLabel->setAutoFillBackground(true);
+ QPalette semiTransparent(QColor(255, 0, 0, 64));
+ semiTransparent.setBrush(QPalette::Text, Qt::white);
+ semiTransparent.setBrush(QPalette::WindowText, Qt::white);
+ overlayLabel->setPalette(semiTransparent);
+ QFont f = overlayLabel->font();
+ f.setPixelSize(QFontInfo(f).pixelSize() * 2);
+ f.setWeight(QFont::Bold);
+ overlayLabel->setFont(f);
+ overlayLabel->resize(320, 320);
+ overlayLabel->hide();
+ QObject::connect(rhiWidget, &ExampleRhiWidget::resized, rhiWidget, [rhiWidget, overlayLabel] {
+ const int w = overlayLabel->width();
+ const int h = overlayLabel->height();
+ overlayLabel->setGeometry(rhiWidget->width() / 2 - w / 2, rhiWidget->height() / 2 - h / 2, w, h);
+ });
+
+ QTextEdit *edit = new QTextEdit(QObject::tr("QRhiWidget!<br><br>"
+ "The cube is textured with QPainter-generated content.<br><br>"
+ "Regular, non-native widgets on top work just fine."));
+ QObject::connect(edit, &QTextEdit::textChanged, edit, [edit, rhiWidget] {
+ rhiWidget->setCubeTextureText(edit->toPlainText());
+ });
+ edit->setMaximumHeight(100);
+ layout->addWidget(edit);
+
+ QSlider *slider = new QSlider(Qt::Horizontal);
+ slider->setMinimum(0);
+ slider->setMaximum(360);
+ QObject::connect(slider, &QSlider::valueChanged, slider, [slider, rhiWidget] {
+ rhiWidget->setCubeRotation(slider->value());
+ });
+
+ QHBoxLayout *sliderLayout = new QHBoxLayout;
+ sliderLayout->addWidget(new QLabel(QObject::tr("Cube rotation")));
+ sliderLayout->addWidget(slider);
+ layout->addLayout(sliderLayout);
+
+ QHBoxLayout *btnLayout = new QHBoxLayout;
+
+ QLabel *apiLabel = new QLabel;
+ btnLayout->addWidget(apiLabel);
+ QObject::connect(rhiWidget, &ExampleRhiWidget::rhiChanged, rhiWidget, [apiLabel](const QString &apiName) {
+ apiLabel->setText(QObject::tr("Using QRhi on ") + apiName);
+ });
+
+ QPushButton *btnMakeWindow = new QPushButton(QObject::tr("Make top-level window"));
+ QObject::connect(btnMakeWindow, &QPushButton::clicked, btnMakeWindow, [rhiWidget, btnMakeWindow, layout] {
+ if (rhiWidget->parentWidget()) {
+ rhiWidget->setParent(nullptr);
+ rhiWidget->setAttribute(Qt::WA_DeleteOnClose, true);
+ rhiWidget->show();
+ btnMakeWindow->setText(QObject::tr("Make child widget"));
+ } else {
+ rhiWidget->setAttribute(Qt::WA_DeleteOnClose, false);
+ layout->addWidget(rhiWidget);
+ btnMakeWindow->setText(QObject::tr("Make top-level window"));
+ }
+ });
+ btnLayout->addWidget(btnMakeWindow);
+
+ QPushButton *btn = new QPushButton(QObject::tr("Grab to image"));
+ QObject::connect(btn, &QPushButton::clicked, btn, [rhiWidget] {
+ QImage image = rhiWidget->grabFramebuffer();
+ qDebug() << "Got image" << image;
+ if (!image.isNull()) {
+ QFileDialog fd(rhiWidget->parentWidget());
+ fd.setAcceptMode(QFileDialog::AcceptSave);
+ fd.setDefaultSuffix("png");
+ fd.selectFile("test.png");
+ if (fd.exec() == QDialog::Accepted)
+ image.save(fd.selectedFiles().first());
+ }
+ });
+ btnLayout->addWidget(btn);
+
+ QCheckBox *cbMsaa = new QCheckBox(QObject::tr("Use 4x MSAA"));
+ QObject::connect(cbMsaa, &QCheckBox::stateChanged, cbMsaa, [cbMsaa, rhiWidget] {
+ if (cbMsaa->isChecked())
+ rhiWidget->setSampleCount(4);
+ else
+ rhiWidget->setSampleCount(1);
+ });
+ btnLayout->addWidget(cbMsaa);
+
+ QCheckBox *cbOvberlay = new QCheckBox(QObject::tr("Show overlay widget"));
+ QObject::connect(cbOvberlay, &QCheckBox::stateChanged, cbOvberlay, [cbOvberlay, overlayLabel] {
+ if (cbOvberlay->isChecked())
+ overlayLabel->setVisible(true);
+ else
+ overlayLabel->setVisible(false);
+ });
+ btnLayout->addWidget(cbOvberlay);
+
+ QCheckBox *cbFlip = new QCheckBox(QObject::tr("Flip"));
+ QObject::connect(cbFlip, &QCheckBox::stateChanged, cbOvberlay, [cbFlip, rhiWidget] {
+ rhiWidget->setMirrorVertically(cbFlip->isChecked());
+ });
+ btnLayout->addWidget(cbFlip);
+
+ QCheckBox *cbFixedSize = new QCheckBox(QObject::tr("Use fixed color buffer size"));
+ btnLayout->addWidget(cbFixedSize);
+ QSlider *fixedSizeSlider = new QSlider(Qt::Horizontal);
+ fixedSizeSlider->setMinimum(16);
+ fixedSizeSlider->setMaximum(512);
+ btnLayout->addWidget(fixedSizeSlider);
+
+ QObject::connect(cbFixedSize, &QCheckBox::stateChanged, cbFixedSize, [cbFixedSize, fixedSizeSlider, rhiWidget] {
+ if (cbFixedSize->isChecked())
+ rhiWidget->setFixedColorBufferSize(QSize(fixedSizeSlider->value(), fixedSizeSlider->value()));
+ else
+ rhiWidget->setFixedColorBufferSize(QSize());
+ });
+ QObject::connect(fixedSizeSlider, &QSlider::valueChanged, fixedSizeSlider, [fixedSizeSlider, cbFixedSize, rhiWidget] {
+ if (cbFixedSize->isChecked())
+ rhiWidget->setFixedColorBufferSize(QSize(fixedSizeSlider->value(), fixedSizeSlider->value()));
+ });
+
+ // Exit when the detached window is closed; there is not much we can do
+ // with the controls in the main window then.
+ QObject::connect(rhiWidget, &QObject::destroyed, rhiWidget, [rhiWidget] {
+ if (!rhiWidget->parentWidget())
+ qGuiApp->quit();
+ });
+
+ layout->addLayout(btnLayout);
+ layout->addWidget(rhiWidget);
+
+ rhiWidget->setCubeTextureText(edit->toPlainText());
+
+ QWidget w;
+ w.setLayout(layout);
+ w.resize(1280, 720);
+ w.show();
+
+ return app.exec();
+}
diff --git a/examples/widgets/rhi/cuberhiwidget/shader_assets/texture.frag.qsb b/examples/widgets/rhi/cuberhiwidget/shader_assets/texture.frag.qsb
new file mode 100644
index 0000000000..dc440d8067
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/shader_assets/texture.frag.qsb
Binary files differ
diff --git a/examples/widgets/rhi/cuberhiwidget/shader_assets/texture.vert.qsb b/examples/widgets/rhi/cuberhiwidget/shader_assets/texture.vert.qsb
new file mode 100644
index 0000000000..84aed7fee2
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/shader_assets/texture.vert.qsb
Binary files differ
diff --git a/examples/widgets/rhi/cuberhiwidget/shaders/texture.frag b/examples/widgets/rhi/cuberhiwidget/shaders/texture.frag
new file mode 100644
index 0000000000..9a14dc6eeb
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/shaders/texture.frag
@@ -0,0 +1,12 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+layout(location = 0) out vec4 fragColor;
+
+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/examples/widgets/rhi/cuberhiwidget/shaders/texture.vert b/examples/widgets/rhi/cuberhiwidget/shaders/texture.vert
new file mode 100644
index 0000000000..a58a932abc
--- /dev/null
+++ b/examples/widgets/rhi/cuberhiwidget/shaders/texture.vert
@@ -0,0 +1,15 @@
+#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;
+};
+
+void main()
+{
+ v_texcoord = vec2(texcoord.x, texcoord.y);
+ gl_Position = mvp * position;
+}