diff options
Diffstat (limited to 'tests/manual')
72 files changed, 2016 insertions, 597 deletions
diff --git a/tests/manual/dialogs/messageboxpanel.cpp b/tests/manual/dialogs/messageboxpanel.cpp index ef6731cd74..07b387f988 100644 --- a/tests/manual/dialogs/messageboxpanel.cpp +++ b/tests/manual/dialogs/messageboxpanel.cpp @@ -133,7 +133,7 @@ void MessageBoxPanel::setupMessageBox(QMessageBox &box) bool ok; QMessageBox::StandardButtons btns = (QMessageBox::StandardButtons) btnHexText.toUInt(&ok, 16); box.setStandardButtons((QMessageBox::StandardButtons) btns); - if (box.standardButtons() == (QMessageBox::StandardButtons) 0) + if (box.standardButtons() == QMessageBox::StandardButtons()) box.setStandardButtons(QMessageBox::Ok); // just to have something. #if QT_VERSION >= 0x050000 diff --git a/tests/manual/dialogs/wizardpanel.cpp b/tests/manual/dialogs/wizardpanel.cpp index 0e5a18fe8c..8e8af5f556 100644 --- a/tests/manual/dialogs/wizardpanel.cpp +++ b/tests/manual/dialogs/wizardpanel.cpp @@ -198,7 +198,7 @@ void WizardOptionsControl::setWizardOptions(int options) class Wizard : public QWizard { Q_OBJECT public: - explicit Wizard(QWidget *parent = 0, Qt::WindowFlags flags = 0); + explicit Wizard(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); public slots: void changeWizardStyle(int newStyle); diff --git a/tests/manual/foreignwindows/main.cpp b/tests/manual/foreignwindows/main.cpp index 44aff54773..5e882b6d9c 100644 --- a/tests/manual/foreignwindows/main.cpp +++ b/tests/manual/foreignwindows/main.cpp @@ -59,9 +59,9 @@ QT_USE_NAMESPACE -typedef QSharedPointer<QWidget> WidgetPtr; -typedef QList<WidgetPtr> WidgetPtrList; -typedef QList<WId> WIdList; +using WidgetPtr = QSharedPointer<QWidget>; +using WidgetPtrList = QVector<WidgetPtr>; +using WIdList = QVector<WId>; // Create some pre-defined Windows controls by class name static WId createInternalWindow(const QString &name) @@ -74,7 +74,7 @@ static WId createInternalWindow(const QString &name) CreateWindowEx(0, reinterpret_cast<const wchar_t *>(name.utf16()), L"NativeCtrl", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - 0, 0, GetModuleHandle(NULL), NULL); + nullptr, nullptr, GetModuleHandle(nullptr), nullptr); if (hwnd) { SetWindowText(hwnd, L"Demo"); result = WId(hwnd); @@ -179,7 +179,7 @@ void WindowDumper::dump() const debug << '#' << n++; if (m_watchedWindows.size() > 1) debug << '\n'; - foreach (const QWindow *w, m_watchedWindows) { + for (const QWindow *w : m_watchedWindows) { const QPoint globalPos = w->mapToGlobal(QPoint()); debug << " " << w << " pos=" << globalPos.x() << ',' << globalPos.y() << '\n'; } @@ -212,7 +212,7 @@ struct EventFilterOption QtDiag::EventFilter::EventCategories categories; }; -EventFilterOption eventFilterOptions[] = { +static EventFilterOption eventFilterOptions[] = { {"mouse-events", "Dump mouse events.", QtDiag::EventFilter::MouseEvents}, {"keyboard-events", "Dump keyboard events.", QtDiag::EventFilter::KeyEvents}, {"state-events", "Dump state/focus change events.", QtDiag::EventFilter::StateChangeEvents | QtDiag::EventFilter::FocusEvents} @@ -248,11 +248,10 @@ int main(int argc, char *argv[]) parser.addOption(outputAllOption); QCommandLineOption continuousOption(QStringList() << QStringLiteral("c") << QStringLiteral("continuous"), QStringLiteral("Output continuously.")); - parser.addOption(outputAllOption); + parser.addOption(continuousOption); QCommandLineOption moveOption(QStringList() << QStringLiteral("m") << QStringLiteral("move"), QStringLiteral("Move window to top left corner.")); parser.addOption(moveOption); - parser.addOption(continuousOption); QCommandLineOption embedOption(QStringList() << QStringLiteral("e") << QStringLiteral("embed"), QStringLiteral("Embed a foreign window into a Qt widget.")); parser.addOption(embedOption); @@ -271,7 +270,7 @@ int main(int argc, char *argv[]) } QWindowList windows; - foreach (const QString &argument, parser.positionalArguments()) { + for (const QString &argument : parser.positionalArguments()) { bool ok = true; WId wid = createInternalWindow(argument); if (!wid) @@ -295,7 +294,7 @@ int main(int argc, char *argv[]) int exitCode = 0; if (parser.isSet(embedOption)) { - QtDiag::EventFilter::EventCategories eventCategories = 0; + QtDiag::EventFilter::EventCategories eventCategories; for (int i = 0; i < eventFilterOptionCount; ++i) { if (parser.isSet(QLatin1String(eventFilterOptions[i].name))) eventCategories |= eventFilterOptions[i].categories; @@ -303,11 +302,11 @@ int main(int argc, char *argv[]) if (eventCategories) app.installEventFilter(new QtDiag::EventFilter(eventCategories, &app)); - const QRect availableGeometry = QApplication::desktop()->availableGeometry(0); + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); QPoint pos = availableGeometry.topLeft() + QPoint(availableGeometry.width(), availableGeometry.height()) / 3; WidgetPtrList mainWindows; - foreach (QWindow *window, windows) { + for (QWindow *window : qAsConst(windows)) { WidgetPtr mainWindow(new EmbeddingWindow(window)); mainWindow->move(pos); mainWindow->resize(availableGeometry.size() / 4); @@ -324,7 +323,6 @@ int main(int argc, char *argv[]) QObject::connect(timer, &QTimer::timeout, &dumper, &WindowDumper::dump); timer->start(1000); exitCode = app.exec(); - } else { WindowDumper(windows).dump(); } diff --git a/tests/manual/qgraphicslayout/flicker/window.h b/tests/manual/qgraphicslayout/flicker/window.h index c49b82704e..b2ebe60af4 100644 --- a/tests/manual/qgraphicslayout/flicker/window.h +++ b/tests/manual/qgraphicslayout/flicker/window.h @@ -74,7 +74,7 @@ class Window; class SlowWidget : public QGraphicsWidget { public: - SlowWidget(QGraphicsWidget *w = 0, Qt::WindowFlags wFlags = 0) : QGraphicsWidget(w, wFlags) + SlowWidget(QGraphicsWidget *w = nullptr, Qt::WindowFlags wFlags = {}) : QGraphicsWidget(w, wFlags) { m_window = 0; } diff --git a/tests/manual/rhi/computebuffer/buffer.comp.qsb b/tests/manual/rhi/computebuffer/buffer.comp.qsb Binary files differindex b1a666f185..6cb7e58365 100644 --- a/tests/manual/rhi/computebuffer/buffer.comp.qsb +++ b/tests/manual/rhi/computebuffer/buffer.comp.qsb diff --git a/tests/manual/rhi/computebuffer/main.frag.qsb b/tests/manual/rhi/computebuffer/main.frag.qsb Binary files differindex f05da411e5..09012b0800 100644 --- a/tests/manual/rhi/computebuffer/main.frag.qsb +++ b/tests/manual/rhi/computebuffer/main.frag.qsb diff --git a/tests/manual/rhi/computebuffer/main.vert.qsb b/tests/manual/rhi/computebuffer/main.vert.qsb Binary files differindex 944645ffbf..7eb200a3da 100644 --- a/tests/manual/rhi/computebuffer/main.vert.qsb +++ b/tests/manual/rhi/computebuffer/main.vert.qsb diff --git a/tests/manual/rhi/computeimage/image.comp.qsb b/tests/manual/rhi/computeimage/image.comp.qsb Binary files differindex 725629f7ab..41a6225063 100644 --- a/tests/manual/rhi/computeimage/image.comp.qsb +++ b/tests/manual/rhi/computeimage/image.comp.qsb diff --git a/tests/manual/rhi/cubemap/cubemap.frag.qsb b/tests/manual/rhi/cubemap/cubemap.frag.qsb Binary files differindex d7cc025554..f63e73e5cb 100644 --- a/tests/manual/rhi/cubemap/cubemap.frag.qsb +++ b/tests/manual/rhi/cubemap/cubemap.frag.qsb diff --git a/tests/manual/rhi/cubemap/cubemap.vert.qsb b/tests/manual/rhi/cubemap/cubemap.vert.qsb Binary files differindex fae136337c..39419d6e46 100644 --- a/tests/manual/rhi/cubemap/cubemap.vert.qsb +++ b/tests/manual/rhi/cubemap/cubemap.vert.qsb diff --git a/tests/manual/rhi/cubemap_render/buildshader.bat b/tests/manual/rhi/cubemap_render/buildshader.bat new file mode 100755 index 0000000000..3886c138d8 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/buildshader.bat @@ -0,0 +1,6 @@ +qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_oneface.vert -o cubemap_oneface.vert.qsb +qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_oneface.frag -o cubemap_oneface.frag.qsb +qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_mrt.vert -o cubemap_mrt.vert.qsb +qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_mrt.frag -o cubemap_mrt.frag.qsb +qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_sample.vert -o cubemap_sample.vert.qsb +qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_sample.frag -o cubemap_sample.frag.qsb diff --git a/tests/manual/rhi/cubemap_render/cubemap_mrt.frag b/tests/manual/rhi/cubemap_render/cubemap_mrt.frag new file mode 100644 index 0000000000..06a08f6cd6 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_mrt.frag @@ -0,0 +1,28 @@ +#version 440 + +layout(location = 0) out vec4 c0; +layout(location = 1) out vec4 c1; +layout(location = 2) out vec4 c2; +layout(location = 3) out vec4 c3; +layout(location = 4) out vec4 c4; +layout(location = 5) out vec4 c5; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; + vec3 color0; + vec3 color1; + vec3 color2; + vec3 color3; + vec3 color4; + vec3 color5; +} ubuf; + +void main() +{ + c0 = vec4(ubuf.color0, 1.0); + c1 = vec4(ubuf.color1, 1.0); + c2 = vec4(ubuf.color2, 1.0); + c3 = vec4(ubuf.color3, 1.0); + c4 = vec4(ubuf.color4, 1.0); + c5 = vec4(ubuf.color5, 1.0); +} diff --git a/tests/manual/rhi/cubemap_render/cubemap_mrt.frag.qsb b/tests/manual/rhi/cubemap_render/cubemap_mrt.frag.qsb Binary files differnew file mode 100644 index 0000000000..7a0be83a81 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_mrt.frag.qsb diff --git a/tests/manual/rhi/cubemap_render/cubemap_mrt.vert b/tests/manual/rhi/cubemap_render/cubemap_mrt.vert new file mode 100644 index 0000000000..6d82feccc8 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_mrt.vert @@ -0,0 +1,20 @@ +#version 440 + +layout(location = 0) in vec4 position; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; + vec3 color0; + vec3 color1; + vec3 color2; + vec3 color3; + vec3 color4; + vec3 color5; +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + gl_Position = ubuf.mvp * position; +} diff --git a/tests/manual/rhi/cubemap_render/cubemap_mrt.vert.qsb b/tests/manual/rhi/cubemap_render/cubemap_mrt.vert.qsb Binary files differnew file mode 100644 index 0000000000..9358ac2792 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_mrt.vert.qsb diff --git a/tests/manual/rhi/cubemap_render/cubemap_oneface.frag b/tests/manual/rhi/cubemap_render/cubemap_oneface.frag new file mode 100644 index 0000000000..8bafe689be --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_oneface.frag @@ -0,0 +1,13 @@ +#version 440 + +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; + vec3 color; +} ubuf; + +void main() +{ + fragColor = vec4(ubuf.color, 1.0); +} diff --git a/tests/manual/rhi/cubemap_render/cubemap_oneface.frag.qsb b/tests/manual/rhi/cubemap_render/cubemap_oneface.frag.qsb Binary files differnew file mode 100644 index 0000000000..a6187939a0 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_oneface.frag.qsb diff --git a/tests/manual/rhi/cubemap_render/cubemap_oneface.vert b/tests/manual/rhi/cubemap_render/cubemap_oneface.vert new file mode 100644 index 0000000000..2db9076a74 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_oneface.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(location = 0) in vec4 position; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; + vec3 color; +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + gl_Position = ubuf.mvp * position; +} diff --git a/tests/manual/rhi/cubemap_render/cubemap_oneface.vert.qsb b/tests/manual/rhi/cubemap_render/cubemap_oneface.vert.qsb Binary files differnew file mode 100644 index 0000000000..58772179e5 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_oneface.vert.qsb diff --git a/tests/manual/rhi/cubemap_render/cubemap_render.cpp b/tests/manual/rhi/cubemap_render/cubemap_render.cpp new file mode 100644 index 0000000000..a08e96f0c6 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_render.cpp @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +// Demonstrates rendering to two cubemaps in two different ways: +// - one by one, to each face, +// - if the supported max number of color attachments is greater than 4: in +// one go with all 6 faces attached as render targets. +// +// Finally, show what we got in a skybox-ish thing. Press the arrow keys to +// switch between the two cubemaps. (the only difference should be their +// background clear color) + +#define EXAMPLEFW_KEYPRESS_EVENTS +#include "../shared/examplefw.h" +#include "../shared/cube.h" + +// each face is 512x512 +static const QSize cubemapSize(512, 512); + +// each cubemap face gets a 256x256 quad in the center +static float halfQuadVertexData[] = +{ // Y up, CCW + -0.5f, 0.5f, + -0.5f, -0.5f, + 0.5f, -0.5f, + 0.5f, 0.5f, +}; + +static quint16 halfQuadIndexData[] = +{ + 0, 1, 2, 0, 2, 3 +}; + +struct { + QVector<QRhiResource *> releasePool; + + QRhiTexture *cubemap1 = nullptr; + QRhiTexture *cubemap2 = nullptr; + bool canDoMrt = false; + + QRhiBuffer *half_quad_vbuf = nullptr; + QRhiBuffer *half_quad_ibuf = nullptr; + + QRhiBuffer *oneface_ubuf = nullptr; + int ubufSizePerFace; + QRhiTextureRenderTarget *oneface_rt[6]; + QRhiRenderPassDescriptor *oneface_rp = nullptr; + QRhiShaderResourceBindings *oneface_srb = nullptr; + QRhiGraphicsPipeline *oneface_ps = nullptr; + + QRhiBuffer *mrt_ubuf = nullptr; + QRhiTextureRenderTarget *mrt_rt = nullptr; + QRhiRenderPassDescriptor *mrt_rp = nullptr; + QRhiShaderResourceBindings *mrt_srb = nullptr; + QRhiGraphicsPipeline *mrt_ps = nullptr; + + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiSampler *sampler = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + QRhiGraphicsPipeline *ps = nullptr; + + QRhiResourceUpdateBatch *initialUpdates = nullptr; + QMatrix4x4 winProj; + float rx = 0; +} d; + +void initializePerFaceRendering(QRhi *rhi) +{ + d.cubemap1 = rhi->newTexture(QRhiTexture::RGBA8, cubemapSize, 1, QRhiTexture::CubeMap | QRhiTexture::RenderTarget); + d.cubemap1->build(); + d.releasePool << d.cubemap1; + + d.ubufSizePerFace = rhi->ubufAligned(64 + 12); + d.oneface_ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.ubufSizePerFace * 6); + d.oneface_ubuf->build(); + d.releasePool << d.oneface_ubuf; + + for (int face = 0; face < 6; ++face) { + QRhiColorAttachment att(d.cubemap1); + att.setLayer(face); + QRhiTextureRenderTargetDescription rtDesc(att); + d.oneface_rt[face] = rhi->newTextureRenderTarget(rtDesc); + if (face == 0) { + d.oneface_rp = d.oneface_rt[0]->newCompatibleRenderPassDescriptor(); + d.releasePool << d.oneface_rp; + } + d.oneface_rt[face]->setRenderPassDescriptor(d.oneface_rp); + d.oneface_rt[face]->build(); + d.releasePool << d.oneface_rt[face]; + } + + d.oneface_srb = rhi->newShaderResourceBindings(); + const QRhiShaderResourceBinding::StageFlags visibility = + QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage; + d.oneface_srb->setBindings({ + QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, visibility, d.oneface_ubuf, 64 + 12) + }); + d.oneface_srb->build(); + d.releasePool << d.oneface_srb; + + d.oneface_ps = rhi->newGraphicsPipeline(); + d.oneface_ps->setShaderStages({ + { QRhiShaderStage::Vertex, getShader(QLatin1String(":/cubemap_oneface.vert.qsb")) }, + { QRhiShaderStage::Fragment, getShader(QLatin1String(":/cubemap_oneface.frag.qsb")) } + }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 2 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + }); + d.oneface_ps->setVertexInputLayout(inputLayout); + d.oneface_ps->setShaderResourceBindings(d.oneface_srb); + d.oneface_ps->setRenderPassDescriptor(d.oneface_rp); + d.oneface_ps->build(); + d.releasePool << d.oneface_ps; + + // wasteful to duplicate the mvp as well but will do for now + for (int face = 0; face < 6; ++face) { + const int offset = d.ubufSizePerFace * face; + QMatrix4x4 identity; + d.initialUpdates->updateDynamicBuffer(d.oneface_ubuf, offset, 64, identity.constData()); + // will use a different color for each face + QColor c; + switch (face) { + case 0: + c = Qt::red; + break; + case 1: + c = Qt::green; + break; + case 2: + c = Qt::blue; + break; + case 3: + c = Qt::yellow; + break; + case 4: + c = Qt::lightGray; + break; + case 5: + c = Qt::cyan; + break; + } + float color[] = { float(c.redF()), float(c.greenF()), float(c.blueF()) }; + d.initialUpdates->updateDynamicBuffer(d.oneface_ubuf, offset + 64, 12, color); + } +} + +// 6 render passes, 1 draw call each, targeting one cubemap face at a time +void renderPerFace(QRhiCommandBuffer *cb) +{ + for (int face = 0; face < 6; ++face) { + cb->beginPass(d.oneface_rt[face], Qt::black, { 1.0f, 0 }); + cb->setGraphicsPipeline(d.oneface_ps); + cb->setViewport({ 0, 0, + float(d.oneface_rt[face]->pixelSize().width()), + float(d.oneface_rt[face]->pixelSize().height()) }); + const QRhiCommandBuffer::DynamicOffset dynamicOffset(0, face * d.ubufSizePerFace); + cb->setShaderResources(nullptr, 1, &dynamicOffset); + QRhiCommandBuffer::VertexInput vbufBinding(d.half_quad_vbuf, 0); + cb->setVertexInput(0, 1, &vbufBinding, d.half_quad_ibuf, 0, QRhiCommandBuffer::IndexUInt16); + cb->drawIndexed(6); + cb->endPass(); + } +} + +void initializeMrtRendering(QRhi *rhi) +{ + d.cubemap2 = rhi->newTexture(QRhiTexture::RGBA8, cubemapSize, 1, QRhiTexture::CubeMap | QRhiTexture::RenderTarget); + d.cubemap2->build(); + d.releasePool << d.cubemap2; + + d.mrt_ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 6 * 16); // note that vec3 is aligned to 16 bytes + d.mrt_ubuf->build(); + d.releasePool << d.mrt_ubuf; + + QVarLengthArray<QRhiColorAttachment, 6> attachments; + for (int face = 0; face < 6; ++face) { + QRhiColorAttachment att(d.cubemap2); + att.setLayer(face); + attachments.append(att); + } + QRhiTextureRenderTargetDescription rtDesc; + rtDesc.setColorAttachments(attachments.cbegin(), attachments.cend()); + d.mrt_rt = rhi->newTextureRenderTarget(rtDesc); + d.mrt_rp = d.mrt_rt->newCompatibleRenderPassDescriptor(); + d.releasePool << d.mrt_rp; + d.mrt_rt->setRenderPassDescriptor(d.mrt_rp); + d.mrt_rt->build(); + d.releasePool << d.mrt_rt; + + d.mrt_srb = rhi->newShaderResourceBindings(); + const QRhiShaderResourceBinding::StageFlags visibility = + QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage; + d.mrt_srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, visibility, d.mrt_ubuf) + }); + d.mrt_srb->build(); + d.releasePool << d.mrt_srb; + + d.mrt_ps = rhi->newGraphicsPipeline(); + d.mrt_ps->setShaderStages({ + { QRhiShaderStage::Vertex, getShader(QLatin1String(":/cubemap_mrt.vert.qsb")) }, + { QRhiShaderStage::Fragment, getShader(QLatin1String(":/cubemap_mrt.frag.qsb")) } + }); + QVarLengthArray<QRhiGraphicsPipeline::TargetBlend, 6> targetBlends; + for (int face = 0; face < 6; ++face) + targetBlends.append({}); // default to blend = false, color write = all, which is good + d.mrt_ps->setTargetBlends(targetBlends.cbegin(), targetBlends.cend()); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 2 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + }); + d.mrt_ps->setVertexInputLayout(inputLayout); + d.mrt_ps->setShaderResourceBindings(d.mrt_srb); + d.mrt_ps->setRenderPassDescriptor(d.mrt_rp); + d.mrt_ps->build(); + d.releasePool << d.mrt_ps; + + QMatrix4x4 identity; + d.initialUpdates->updateDynamicBuffer(d.mrt_ubuf, 0, 64, identity.constData()); + for (int face = 0; face < 6; ++face) { + const int offset = 64 + face * 16; + // will use a different color for each face + QColor c; + switch (face) { + case 0: + c = Qt::red; + break; + case 1: + c = Qt::green; + break; + case 2: + c = Qt::blue; + break; + case 3: + c = Qt::yellow; + break; + case 4: + c = Qt::lightGray; + break; + case 5: + c = Qt::cyan; + break; + } + float color[] = { float(c.redF()), float(c.greenF()), float(c.blueF()) }; + d.initialUpdates->updateDynamicBuffer(d.mrt_ubuf, offset, 12, color); + } +} + +// 1 render pass, 1 draw call, with all 6 faces attached and written to +void renderWithMrt(QRhiCommandBuffer *cb) +{ + // use a different clear color to differentiate from cubemap1 (because the + // results are expected to be identical otherwise) + cb->beginPass(d.mrt_rt, Qt::magenta, { 1.0f, 0 }); + cb->setGraphicsPipeline(d.mrt_ps); + cb->setViewport({ 0, 0, + float(d.mrt_rt->pixelSize().width()), + float(d.mrt_rt->pixelSize().height()) }); + cb->setShaderResources(); + QRhiCommandBuffer::VertexInput vbufBinding(d.half_quad_vbuf, 0); + cb->setVertexInput(0, 1, &vbufBinding, d.half_quad_ibuf, 0, QRhiCommandBuffer::IndexUInt16); + cb->drawIndexed(6); + cb->endPass(); +} + +void Window::customInit() +{ + d.half_quad_vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(halfQuadVertexData)); + d.half_quad_vbuf->build(); + d.releasePool << d.half_quad_vbuf; + + d.half_quad_ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(halfQuadIndexData)); + d.half_quad_ibuf->build(); + d.releasePool << d.half_quad_ibuf; + + d.initialUpdates = m_r->nextResourceUpdateBatch(); + d.initialUpdates->uploadStaticBuffer(d.half_quad_vbuf, 0, sizeof(halfQuadVertexData), halfQuadVertexData); + d.initialUpdates->uploadStaticBuffer(d.half_quad_ibuf, halfQuadIndexData); + + initializePerFaceRendering(m_r); + + d.canDoMrt = m_r->resourceLimit(QRhi::MaxColorAttachments) >= 6; + if (d.canDoMrt) + initializeMrtRendering(m_r); + else + qWarning("Not enough color attachments (need 6, supports %d)", m_r->resourceLimit(QRhi::MaxColorAttachments)); + + + // onscreen stuff + d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube)); + d.vbuf->build(); + d.releasePool << d.vbuf; + d.initialUpdates->uploadStaticBuffer(d.vbuf, cube); + + d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64); + d.ubuf->build(); + d.releasePool << d.ubuf; + + d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, + QRhiSampler::Repeat, QRhiSampler::Repeat); + d.sampler->build(); + d.releasePool << d.sampler; + + d.srb = m_r->newShaderResourceBindings(); + d.srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap1, d.sampler) + }); + d.srb->build(); + d.releasePool << d.srb; + + d.ps = m_r->newGraphicsPipeline(); + d.ps->setDepthTest(true); + d.ps->setDepthWrite(true); + d.ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual); + d.ps->setCullMode(QRhiGraphicsPipeline::Front); // we are inside the cube so cull front, not back + d.ps->setFrontFace(QRhiGraphicsPipeline::CCW); // front is ccw in the cube data + QShader vs = getShader(QLatin1String(":/cubemap_sample.vert.qsb")); + Q_ASSERT(vs.isValid()); + QShader fs = getShader(QLatin1String(":/cubemap_sample.frag.qsb")); + Q_ASSERT(fs.isValid()); + d.ps->setShaderStages({ + { QRhiShaderStage::Vertex, vs }, + { QRhiShaderStage::Fragment, fs } + }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 3 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float3, 0 } + }); + d.ps->setVertexInputLayout(inputLayout); + d.ps->setShaderResourceBindings(d.srb); + d.ps->setRenderPassDescriptor(m_rp); + d.ps->build(); + d.releasePool << d.ps; + + if (d.canDoMrt) + qDebug("Use the arrow keys to switch between the two generated cubemaps"); +} + +void Window::customRelease() +{ + qDeleteAll(d.releasePool); + d.releasePool.clear(); +} + +void Window::customRender() +{ + const QSize outputSizeInPixels = m_sc->currentPixelSize(); + QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer(); + QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch(); + if (d.initialUpdates) { + u->merge(d.initialUpdates); + d.initialUpdates->release(); + d.initialUpdates = nullptr; + } + + QMatrix4x4 mvp = m_r->clipSpaceCorrMatrix(); + mvp.perspective(90.0f, outputSizeInPixels.width() / (float) outputSizeInPixels.height(), 0.01f, 1000.0f); + mvp.scale(10); + mvp.rotate(d.rx, 1, 0, 0); + d.rx += 0.5f; + u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData()); + + cb->resourceUpdate(u); + + renderPerFace(cb); + + if (d.canDoMrt) + renderWithMrt(cb); + + cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }); + cb->setGraphicsPipeline(d.ps); + cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height())); + cb->setShaderResources(); + const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(36); + cb->endPass(); +} + +void Window::keyPressEvent(QKeyEvent *e) +{ + switch (e->key()) { + case Qt::Key_Left: + case Qt::Key_Up: + qDebug("Showing first cubemap (generated by rendering to the faces one by one; black background)"); + d.srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap1, d.sampler) + }); + d.srb->build(); + break; + case Qt::Key_Right: + case Qt::Key_Down: + if (d.canDoMrt) { + qDebug("Showing second cubemap (generated with multiple render targets; magenta background)"); + d.srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap2, d.sampler) + }); + d.srb->build(); + } + break; + default: + e->ignore(); + break; + } +} diff --git a/tests/manual/rhi/cubemap_render/cubemap_render.pro b/tests/manual/rhi/cubemap_render/cubemap_render.pro new file mode 100644 index 0000000000..71208cc01f --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_render.pro @@ -0,0 +1,8 @@ +TEMPLATE = app + +QT += gui-private + +SOURCES = \ + cubemap_render.cpp + +RESOURCES = cubemap_render.qrc diff --git a/tests/manual/rhi/cubemap_render/cubemap_render.qrc b/tests/manual/rhi/cubemap_render/cubemap_render.qrc new file mode 100644 index 0000000000..f6029d3f3a --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_render.qrc @@ -0,0 +1,10 @@ +<!DOCTYPE RCC><RCC version="1.0"> + <qresource> + <file>cubemap_oneface.vert.qsb</file> + <file>cubemap_oneface.frag.qsb</file> + <file>cubemap_mrt.vert.qsb</file> + <file>cubemap_mrt.frag.qsb</file> + <file>cubemap_sample.vert.qsb</file> + <file>cubemap_sample.frag.qsb</file> +</qresource> +</RCC> diff --git a/tests/manual/rhi/cubemap_render/cubemap_sample.frag b/tests/manual/rhi/cubemap_render/cubemap_sample.frag new file mode 100644 index 0000000000..13a365ed0c --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_sample.frag @@ -0,0 +1,10 @@ +#version 440 + +layout(location = 0) in vec3 v_coord; +layout(location = 0) out vec4 fragColor; +layout(binding = 1) uniform samplerCube tex; + +void main() +{ + fragColor = vec4(texture(tex, v_coord).rgb, 1.0); +} diff --git a/tests/manual/rhi/cubemap_render/cubemap_sample.frag.qsb b/tests/manual/rhi/cubemap_render/cubemap_sample.frag.qsb Binary files differnew file mode 100644 index 0000000000..f26e3985b6 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_sample.frag.qsb diff --git a/tests/manual/rhi/cubemap_render/cubemap_sample.vert b/tests/manual/rhi/cubemap_render/cubemap_sample.vert new file mode 100644 index 0000000000..f1caf1a74f --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_sample.vert @@ -0,0 +1,16 @@ +#version 440 + +layout(location = 0) in vec4 position; +layout(location = 0) out vec3 v_coord; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + v_coord = position.xyz; + gl_Position = ubuf.mvp * position; +} diff --git a/tests/manual/rhi/cubemap_render/cubemap_sample.vert.qsb b/tests/manual/rhi/cubemap_render/cubemap_sample.vert.qsb Binary files differnew file mode 100644 index 0000000000..0cbc144011 --- /dev/null +++ b/tests/manual/rhi/cubemap_render/cubemap_sample.vert.qsb diff --git a/tests/manual/rhi/float16texture_with_compute/buildshaders.sh b/tests/manual/rhi/float16texture_with_compute/buildshaders.sh new file mode 100755 index 0000000000..7d4d27741d --- /dev/null +++ b/tests/manual/rhi/float16texture_with_compute/buildshaders.sh @@ -0,0 +1,3 @@ +#!/bin/sh +qsb --glsl "430,310 es" --hlsl 50 --msl 12 load.comp -o load.comp.qsb +qsb --glsl "430,310 es" --hlsl 50 --msl 12 prefilter.comp -o prefilter.comp.qsb diff --git a/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.cpp b/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.cpp new file mode 100644 index 0000000000..9d74fe797b --- /dev/null +++ b/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +// An advanced version of floattexture. Instead of RGBA32F, we use RGBA16F, and +// also generate the floating point data from rgba with compute. Then there's a +// compute pass using the BSDF prefiltering taken from Qt Quick 3D, which +// generates all the mip levels. + +// Why do we animate the scale of the quad rendered to the window? To have +// different mip levels used, to prove that all of them are generated +// correctly, without artifacts (which would occur if memory barriers were not +// correctly generated by QRhi). For full verification use RenderDoc or similar. + +#include "../shared/examplefw.h" +#include <qmath.h> + +static float vertexData[] = +{ // Y up, CCW + -0.5f, 0.5f, 0.0f, 0.0f, + -0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, 0.5f, 1.0f, 0.0f +}; + +static quint16 indexData[] = +{ + 0, 1, 2, 0, 2, 3 +}; + +static const int MAX_MIP_LEVELS = 20; + +struct { + QVector<QRhiResource *> releasePool; + + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ibuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiTexture *texRgba = nullptr; + QRhiTexture *texFloat16 = nullptr; + QRhiSampler *sampler = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + QRhiGraphicsPipeline *ps = nullptr; + + QRhiBuffer *computeUBuf_load = nullptr; + QRhiShaderResourceBindings *computeBindings_load = nullptr; + QRhiComputePipeline *computePipeline_load = nullptr; + QRhiBuffer *computeUBuf_prefilter = nullptr; + QRhiShaderResourceBindings *computeBindings_prefilter[MAX_MIP_LEVELS]; + QRhiComputePipeline *computePipeline_prefilter = nullptr; + + QRhiResourceUpdateBatch *initialUpdates = nullptr; + bool computeDone = false; + int mipCount; + int prefilterUBufElemSize; + quint32 prefilterNumWorkGroups[MAX_MIP_LEVELS][3]; + float scale = 2.5f; + int scale_dir = -1; +} d; + +void recordUploadThenFilterFloat16TextureWithCompute(QRhiCommandBuffer *cb) +{ + const int w = d.texRgba->pixelSize().width() / 16; + const int h = d.texRgba->pixelSize().height() / 16; + + cb->beginComputePass(); + + cb->setComputePipeline(d.computePipeline_load); + cb->setShaderResources(); + cb->dispatch(w, h, 1); + + cb->setComputePipeline(d.computePipeline_prefilter); + for (int level = 1; level < d.mipCount; ++level) { + const int i = level - 1; + const int mipW = d.prefilterNumWorkGroups[i][0]; + const int mipH = d.prefilterNumWorkGroups[i][1]; + QPair<int, quint32> dynamicOffset = { 0, quint32(d.prefilterUBufElemSize * i) }; + cb->setShaderResources(d.computeBindings_prefilter[i], 1, &dynamicOffset); + cb->dispatch(mipW, mipH, 1); + } + + cb->endComputePass(); +} + +void Window::customInit() +{ + if (!m_r->isFeatureSupported(QRhi::Compute)) + qFatal("Compute is not supported"); + + if (!m_r->isTextureFormatSupported(QRhiTexture::RGBA16F)) + qFatal("RGBA16F texture format is not supported"); + + d.initialUpdates = m_r->nextResourceUpdateBatch(); + + // load rgba8 image data + + QImage image; + image.load(QLatin1String(":/qt256.png")); + image = image.convertToFormat(QImage::Format_RGBA8888); + Q_ASSERT(!image.isNull()); + d.texRgba = m_r->newTexture(QRhiTexture::RGBA8, image.size(), 1, QRhiTexture::UsedWithLoadStore); + d.texRgba->build(); + d.releasePool << d.texRgba; + + d.initialUpdates->uploadTexture(d.texRgba, image); + + d.mipCount = m_r->mipLevelsForSize(image.size()); + Q_ASSERT(d.mipCount <= MAX_MIP_LEVELS); + + d.texFloat16 = m_r->newTexture(QRhiTexture::RGBA16F, image.size(), 1, QRhiTexture::UsedWithLoadStore | QRhiTexture::MipMapped); + d.releasePool << d.texFloat16; + d.texFloat16->build(); + + // compute + + d.computeUBuf_load = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 12); + d.computeUBuf_load->build(); + d.releasePool << d.computeUBuf_load; + + quint32 numWorkGroups[3] = { quint32(image.width()), quint32(image.height()), 0 }; + d.initialUpdates->updateDynamicBuffer(d.computeUBuf_load, 0, 12, numWorkGroups); + + d.computeBindings_load = m_r->newShaderResourceBindings(); + d.computeBindings_load->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::ComputeStage, d.computeUBuf_load), + QRhiShaderResourceBinding::imageLoad(1, QRhiShaderResourceBinding::ComputeStage, d.texRgba, 0), + QRhiShaderResourceBinding::imageStore(2, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, 0) + }); + d.computeBindings_load->build(); + d.releasePool << d.computeBindings_load; + + d.computePipeline_load = m_r->newComputePipeline(); + d.computePipeline_load->setShaderResourceBindings(d.computeBindings_load); + d.computePipeline_load->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/load.comp.qsb")) }); + d.computePipeline_load->build(); + d.releasePool << d.computePipeline_load; + + d.prefilterUBufElemSize = m_r->ubufAligned(12); + d.computeUBuf_prefilter = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.prefilterUBufElemSize * d.mipCount); + d.computeUBuf_prefilter->build(); + d.releasePool << d.computeUBuf_prefilter; + + int mipW = image.width() >> 1; + int mipH = image.height() >> 1; + for (int level = 1; level < d.mipCount; ++level) { + const int i = level - 1; + d.prefilterNumWorkGroups[i][0] = quint32(mipW); + d.prefilterNumWorkGroups[i][1] = quint32(mipH); + d.prefilterNumWorkGroups[i][2] = 0; + d.initialUpdates->updateDynamicBuffer(d.computeUBuf_prefilter, d.prefilterUBufElemSize * i, 12, d.prefilterNumWorkGroups[i]); + mipW = mipW > 2 ? mipW >> 1 : 1; + mipH = mipH > 2 ? mipH >> 1 : 1; + + d.computeBindings_prefilter[i] = m_r->newShaderResourceBindings(); + d.computeBindings_prefilter[i]->setBindings({ + QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::ComputeStage, d.computeUBuf_prefilter, 12), + QRhiShaderResourceBinding::imageLoad(1, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, level - 1), + QRhiShaderResourceBinding::imageStore(2, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, level) + }); + d.computeBindings_prefilter[i]->build(); + d.releasePool << d.computeBindings_prefilter[i]; + } + + d.computePipeline_prefilter = m_r->newComputePipeline(); + d.computePipeline_prefilter->setShaderResourceBindings(d.computeBindings_prefilter[0]); // just need a layout compatible one + d.computePipeline_prefilter->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/prefilter.comp.qsb")) }); + d.computePipeline_prefilter->build(); + d.releasePool << d.computePipeline_prefilter; + + // graphics + + d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)); + d.vbuf->build(); + d.releasePool << d.vbuf; + + d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData)); + d.ibuf->build(); + d.releasePool << d.ibuf; + + d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68); + d.ubuf->build(); + d.releasePool << d.ubuf; + + // enable mipmaps + d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge); + d.releasePool << d.sampler; + d.sampler->build(); + + d.srb = m_r->newShaderResourceBindings(); + d.releasePool << d.srb; + d.srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.texFloat16, d.sampler) + }); + d.srb->build(); + + d.ps = m_r->newGraphicsPipeline(); + d.releasePool << d.ps; + d.ps->setShaderStages({ + { QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) }, + { QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) } + }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 4 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) } + }); + d.ps->setVertexInputLayout(inputLayout); + d.ps->setShaderResourceBindings(d.srb); + d.ps->setRenderPassDescriptor(m_rp); + d.ps->build(); + + d.initialUpdates->uploadStaticBuffer(d.vbuf, vertexData); + d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData); + + qint32 flip = 0; + d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip); +} + +void Window::customRelease() +{ + qDeleteAll(d.releasePool); + d.releasePool.clear(); +} + +void Window::customRender() +{ + QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer(); + QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch(); + if (d.initialUpdates) { + u->merge(d.initialUpdates); + d.initialUpdates->release(); + d.initialUpdates = nullptr; + } + + QMatrix4x4 mvp = m_proj; + mvp.scale(d.scale); + d.scale += d.scale_dir * 0.01f; + if (qFuzzyIsNull(d.scale) || d.scale >= 2.5f) + d.scale_dir *= -1; + u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData()); + + cb->resourceUpdate(u); + + // If not yet done, then do a compute pass that uploads level 0, doing an + // rgba8 -> float16 conversion. Follow that with another compute pass to do + // the filtering and generate all the mip levels. + if (!d.computeDone) { + recordUploadThenFilterFloat16TextureWithCompute(cb); + d.computeDone = true; + } + + const QSize outputSizeInPixels = m_sc->currentPixelSize(); + cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }); + cb->setGraphicsPipeline(d.ps); + cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) }); + cb->setShaderResources(); + const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0); + cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16); + cb->drawIndexed(6); + cb->endPass(); +} diff --git a/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.pro b/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.pro new file mode 100644 index 0000000000..7708e845d0 --- /dev/null +++ b/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.pro @@ -0,0 +1,8 @@ +TEMPLATE = app + +QT += gui-private + +SOURCES = \ + float16texture_with_compute.cpp + +RESOURCES = float16texture_with_compute.qrc diff --git a/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.qrc b/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.qrc new file mode 100644 index 0000000000..ce3c52aac5 --- /dev/null +++ b/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.qrc @@ -0,0 +1,9 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>load.comp.qsb</file> + <file>prefilter.comp.qsb</file> + <file alias="texture.vert.qsb">../shared/texture.vert.qsb</file> + <file alias="texture.frag.qsb">../shared/texture.frag.qsb</file> + <file alias="qt256.png">../shared/qt256.png</file> +</qresource> +</RCC> diff --git a/tests/manual/rhi/float16texture_with_compute/load.comp b/tests/manual/rhi/float16texture_with_compute/load.comp new file mode 100644 index 0000000000..eb7bc1ece4 --- /dev/null +++ b/tests/manual/rhi/float16texture_with_compute/load.comp @@ -0,0 +1,19 @@ +#version 440 + +layout(local_size_x = 16, local_size_y = 16) in; +layout(rgba8, binding = 1) readonly uniform image2D inputImage; +layout(rgba16f, binding = 2) writeonly uniform image2D outputImage; + +// There is no equivalent of gl_NumWorkGroups in HLSL. So instead pass the +// values in in a uniform buffer. +layout(std140, binding = 0) uniform numWorkGroupsBuf { + uvec3 numWorkGroups; +}; + +void main() +{ + if (gl_GlobalInvocationID.x >= numWorkGroups.x || gl_GlobalInvocationID.y >= numWorkGroups.y) + return; + vec4 value = imageLoad(inputImage, ivec2(gl_GlobalInvocationID.xy)); + imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), value); +} diff --git a/tests/manual/rhi/float16texture_with_compute/load.comp.qsb b/tests/manual/rhi/float16texture_with_compute/load.comp.qsb Binary files differnew file mode 100644 index 0000000000..bfdd7f5446 --- /dev/null +++ b/tests/manual/rhi/float16texture_with_compute/load.comp.qsb diff --git a/tests/manual/rhi/float16texture_with_compute/prefilter.comp b/tests/manual/rhi/float16texture_with_compute/prefilter.comp new file mode 100644 index 0000000000..ba09ecb129 --- /dev/null +++ b/tests/manual/rhi/float16texture_with_compute/prefilter.comp @@ -0,0 +1,50 @@ +#version 440 + +layout(local_size_x = 16, local_size_y = 16) in; +layout(rgba16f, binding = 1) readonly uniform image2D inputImage; +layout(rgba16f, binding = 2) writeonly uniform image2D outputImage; + +// There is no equivalent of gl_NumWorkGroups in HLSL. So instead pass the +// values in in a uniform buffer. +layout(std140, binding = 0) uniform numWorkGroupsBuf { + uvec3 numWorkGroups; +}; + +int wrapMod( in int a, in int base ) +{ + return ( a >= 0 ) ? a % base : -(a % base) + base; +} + +void getWrappedCoords( inout int sX, inout int sY, in int width, in int height ) +{ + if (sY < 0) { sX -= width >> 1; sY = -sY; } + if (sY >= height) { sX += width >> 1; sY = height - sY; } + sX = wrapMod( sX, width ); +} + +void main() +{ + int prevWidth = int(numWorkGroups.x) << 1; + int prevHeight = int(numWorkGroups.y) << 1; + if (gl_GlobalInvocationID.x >= numWorkGroups.x || gl_GlobalInvocationID.y >= numWorkGroups.y) + return; + vec4 accumVal = vec4(0.0); + for (int sy = -2; sy <= 2; ++sy) { + for (int sx = -2; sx <= 2; ++sx) { + int sampleX = sx + (int(gl_GlobalInvocationID.x) << 1); + int sampleY = sy + (int(gl_GlobalInvocationID.y) << 1); + getWrappedCoords(sampleX, sampleY, prevWidth, prevHeight); + if ((sampleY * prevWidth + sampleX) < 0 ) + sampleY = prevHeight + sampleY; + ivec2 pos = ivec2(sampleX, sampleY); + vec4 value = imageLoad(inputImage, pos); + float filterPdf = 1.0 / ( 1.0 + float(sx*sx + sy*sy)*2.0 ); + filterPdf /= 4.71238898; + accumVal[0] += filterPdf * value.r; + accumVal[1] += filterPdf * value.g; + accumVal[2] += filterPdf * value.b; + accumVal[3] += filterPdf * value.a; + } + } + imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), accumVal); +} diff --git a/tests/manual/rhi/float16texture_with_compute/prefilter.comp.qsb b/tests/manual/rhi/float16texture_with_compute/prefilter.comp.qsb Binary files differnew file mode 100644 index 0000000000..77618a12b9 --- /dev/null +++ b/tests/manual/rhi/float16texture_with_compute/prefilter.comp.qsb diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.cpp b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.cpp deleted file mode 100644 index ea1fefc308..0000000000 --- a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -// This is a compact, minimal, single-file demo of deciding the backend at -// runtime while using the exact same shaders and rendering code without any -// branching whatsoever once the QWindow is up and the RHI is initialized. - -#include <QGuiApplication> -#include <QCommandLineParser> -#include <QWindow> -#include <QPlatformSurfaceEvent> -#include <QElapsedTimer> - -#include <QtGui/private/qshader_p.h> -#include <QFile> - -#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 - -static float vertexData[] = { - // Y up (note clipSpaceCorrMatrix in m_proj), CCW - 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, - -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, - 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, -}; - -static QShader getShader(const QString &name) -{ - QFile f(name); - if (f.open(QIODevice::ReadOnly)) - return QShader::fromSerialized(f.readAll()); - - return QShader(); -} - -enum GraphicsApi -{ - OpenGL, - Vulkan, - D3D11, - Metal, - Null -}; - -static GraphicsApi graphicsApi; - -static QString graphicsApiName() -{ - switch (graphicsApi) { - case OpenGL: - return QLatin1String("OpenGL 2.x"); - case Vulkan: - return QLatin1String("Vulkan"); - case D3D11: - return QLatin1String("Direct3D 11"); - case Metal: - return QLatin1String("Metal"); - case Null: - return QLatin1String("Null"); - default: - break; - } - return QString(); -} - -class Window : public QWindow -{ -public: - Window(); - ~Window(); - - void init(); - void releaseResources(); - void resizeSwapChain(); - void releaseSwapChain(); - void render(); - - void exposeEvent(QExposeEvent *) override; - bool event(QEvent *) override; - -private: - 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; - QRhiBuffer *m_vbuf = nullptr; - bool m_vbufReady = false; - QRhiBuffer *m_ubuf = nullptr; - QRhiShaderResourceBindings *m_srb = nullptr; - QRhiGraphicsPipeline *m_ps = nullptr; - QVector<QRhiResource *> releasePool; - - QMatrix4x4 m_proj; - float m_rotation = 0; - float m_opacity = 1; - int m_opacityDir = -1; - - QElapsedTimer m_timer; - qint64 m_elapsedMs; - int m_elapsedCount; - -#ifndef QT_NO_OPENGL - QOffscreenSurface *m_fallbackSurface = nullptr; -#endif -}; - -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(); - } - - // stop pushing frames when not exposed (or size is 0) - if ((!isExposed() || (m_hasSwapChain && m_sc->surfacePixelSize().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 && !m_sc->surfacePixelSize().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() && !m_sc->surfacePixelSize().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, ¶ms); - } - -#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, ¶ms); - } -#endif - -#if QT_CONFIG(vulkan) - if (graphicsApi == Vulkan) { - QRhiVulkanInitParams params; - params.inst = vulkanInstance(); - params.window = this; - m_r = QRhi::create(QRhi::Vulkan, ¶ms); - } -#endif - -#ifdef Q_OS_WIN - if (graphicsApi == D3D11) { - QRhiD3D11InitParams params; - m_r = QRhi::create(QRhi::D3D11, ¶ms); - } -#endif - -#ifdef Q_OS_DARWIN - if (graphicsApi == Metal) { - QRhiMetalInitParams params; - m_r = QRhi::create(QRhi::Metal, ¶ms); - } -#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 here, due to UsedWithSwapChainOnly - 1, - QRhiRenderBuffer::UsedWithSwapChainOnly); - releasePool << m_ds; - m_sc->setWindow(this); - m_sc->setDepthStencil(m_ds); - m_rp = m_sc->newCompatibleRenderPassDescriptor(); - releasePool << m_rp; - m_sc->setRenderPassDescriptor(m_rp); - - m_vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)); - releasePool << m_vbuf; - m_vbuf->build(); - m_vbufReady = false; - - m_ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68); - releasePool << m_ubuf; - m_ubuf->build(); - - m_srb = m_r->newShaderResourceBindings(); - releasePool << m_srb; - m_srb->setBindings({ - QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_ubuf) - }); - m_srb->build(); - - m_ps = m_r->newGraphicsPipeline(); - releasePool << m_ps; - - QRhiGraphicsPipeline::TargetBlend premulAlphaBlend; - premulAlphaBlend.enable = true; - m_ps->setTargetBlends({ premulAlphaBlend }); - - const QShader vs = getShader(QLatin1String(":/color.vert.qsb")); - if (!vs.isValid()) - qFatal("Failed to load shader pack (vertex)"); - const QShader fs = getShader(QLatin1String(":/color.frag.qsb")); - if (!fs.isValid()) - qFatal("Failed to load shader pack (fragment)"); - - m_ps->setShaderStages({ - { QRhiShaderStage::Vertex, vs }, - { QRhiShaderStage::Fragment, fs } - }); - - QRhiVertexInputLayout inputLayout; - inputLayout.setBindings({ - { 5 * sizeof(float) } - }); - inputLayout.setAttributes({ - { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, - { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) } - }); - - m_ps->setVertexInputLayout(inputLayout); - m_ps->setShaderResourceBindings(m_srb); - m_ps->setRenderPassDescriptor(m_rp); - - m_ps->build(); -} - -void Window::releaseResources() -{ - qDeleteAll(releasePool); - releasePool.clear(); - - delete m_sc; // native swapchain is likely released already - m_sc = nullptr; - - delete m_r; - -#ifndef QT_NO_OPENGL - delete m_fallbackSurface; -#endif -} - -void Window::resizeSwapChain() -{ - m_hasSwapChain = m_sc->buildOrResize(); // also handles m_ds - - m_elapsedMs = 0; - m_elapsedCount = 0; - - const QSize outputSize = m_sc->currentPixelSize(); - m_proj = m_r->clipSpaceCorrMatrix(); - m_proj.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 100.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; - } - - if (m_elapsedCount) - m_elapsedMs += m_timer.elapsed(); - m_timer.restart(); - m_elapsedCount += 1; - if (m_elapsedMs >= 4000) { - qDebug("%f", m_elapsedCount / 4.0f); - m_elapsedMs = 0; - m_elapsedCount = 0; - } - - // Set up buffer updates. - QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch(); - if (!m_vbufReady) { - m_vbufReady = true; - u->uploadStaticBuffer(m_vbuf, vertexData); - } - m_rotation += 1.0f; - QMatrix4x4 mvp = m_proj; - mvp.rotate(m_rotation, 0, 1, 0); - u->updateDynamicBuffer(m_ubuf, 0, 64, mvp.constData()); - m_opacity += m_opacityDir * 0.005f; - if (m_opacity < 0.0f || m_opacity > 1.0f) { - m_opacityDir *= -1; - m_opacity = qBound(0.0f, m_opacity, 1.0f); - } - u->updateDynamicBuffer(m_ubuf, 64, 4, &m_opacity); - - QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer(); - const QSize outputSizeInPixels = m_sc->currentPixelSize(); - - // Apply buffer updates, clear, start the renderpass (where applicable). - cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u); - - cb->setGraphicsPipeline(m_ps); - cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) }); - cb->setShaderResources(); - - const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf, 0); - cb->setVertexInput(0, 1, &vbufBinding); - cb->draw(3); - - cb->endPass(); - - // Submit. - m_r->endFrame(m_sc); - - requestUpdate(); // render continuously, throttled by the presentation rate (due to beginFrame above) -} - -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 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); - QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null")); - cmdLineParser.addOption(nullOption); - cmdLineParser.process(app); - if (cmdLineParser.isSet(glOption)) - graphicsApi = OpenGL; - if (cmdLineParser.isSet(vkOption)) - graphicsApi = Vulkan; - if (cmdLineParser.isSet(d3dOption)) - graphicsApi = D3D11; - if (cmdLineParser.isSet(mtlOption)) - graphicsApi = Metal; - if (cmdLineParser.isSet(nullOption)) - graphicsApi = Null; - - // Vulkan setup. -#if QT_CONFIG(vulkan) - QVulkanInstance inst; - if (graphicsApi == Vulkan) { - 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(graphicsApiName()); - w.show(); - - // 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 app.exec(); -} diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.pro b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.pro index eeef33ff6a..a1093fdaec 100644 --- a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.pro +++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.pro @@ -1,8 +1,14 @@ TEMPLATE = app - +CONFIG += console QT += gui-private SOURCES = \ - hellominimalcrossgfxtriangle.cpp + main.cpp \ + window.cpp \ + hellowindow.cpp + +HEADERS = \ + window.h \ + hellowindow.h RESOURCES = hellominimalcrossgfxtriangle.qrc diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp new file mode 100644 index 0000000000..96a088adf4 --- /dev/null +++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#include "hellowindow.h" +#include <QFile> +#include <QtGui/private/qshader_p.h> + +static float vertexData[] = { + // Y up (note clipSpaceCorrMatrix in m_proj), CCW + 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, + -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, +}; + +HelloWindow::HelloWindow(QRhi::Implementation graphicsApi) + : Window(graphicsApi) +{ +} + +QShader HelloWindow::getShader(const QString &name) +{ + QFile f(name); + if (f.open(QIODevice::ReadOnly)) + return QShader::fromSerialized(f.readAll()); + + return QShader(); +} + +void HelloWindow::customInit() +{ + m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData))); + m_vbuf->build(); + m_vbufReady = false; + + m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68)); + m_ubuf->build(); + + m_srb.reset(m_rhi->newShaderResourceBindings()); + m_srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, + m_ubuf.get()) + }); + m_srb->build(); + + m_ps.reset(m_rhi->newGraphicsPipeline()); + + QRhiGraphicsPipeline::TargetBlend premulAlphaBlend; + premulAlphaBlend.enable = true; + m_ps->setTargetBlends({ premulAlphaBlend }); + + const QShader vs = getShader(QLatin1String(":/color.vert.qsb")); + if (!vs.isValid()) + qFatal("Failed to load shader pack (vertex)"); + const QShader fs = getShader(QLatin1String(":/color.frag.qsb")); + if (!fs.isValid()) + qFatal("Failed to load shader pack (fragment)"); + + m_ps->setShaderStages({ + { QRhiShaderStage::Vertex, vs }, + { QRhiShaderStage::Fragment, fs } + }); + + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 5 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) } + }); + + m_ps->setVertexInputLayout(inputLayout); + m_ps->setShaderResourceBindings(m_srb.get()); + m_ps->setRenderPassDescriptor(m_rp.get()); + + m_ps->build(); +} + +// called once per frame +void HelloWindow::customRender() +{ + QRhiResourceUpdateBatch *u = m_rhi->nextResourceUpdateBatch(); + if (!m_vbufReady) { + m_vbufReady = true; + u->uploadStaticBuffer(m_vbuf.get(), vertexData); + } + m_rotation += 1.0f; + QMatrix4x4 mvp = m_proj; + mvp.rotate(m_rotation, 0, 1, 0); + u->updateDynamicBuffer(m_ubuf.get(), 0, 64, mvp.constData()); + m_opacity += m_opacityDir * 0.005f; + if (m_opacity < 0.0f || m_opacity > 1.0f) { + m_opacityDir *= -1; + m_opacity = qBound(0.0f, m_opacity, 1.0f); + } + u->updateDynamicBuffer(m_ubuf.get(), 64, 4, &m_opacity); + + QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer(); + const QSize outputSizeInPixels = m_sc->currentPixelSize(); + + cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u); + + cb->setGraphicsPipeline(m_ps.get()); + cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) }); + cb->setShaderResources(); + + const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(3); + + cb->endPass(); +} diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h new file mode 100644 index 0000000000..dd9890f11c --- /dev/null +++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef HELLOWINDOW_H +#define HELLOWINDOW_H + +#include "window.h" + +class HelloWindow : public Window +{ +public: + HelloWindow(QRhi::Implementation graphicsApi); + + void customInit() override; + void customRender() override; + +private: + QShader getShader(const QString &name); + + std::unique_ptr<QRhiBuffer> m_vbuf; + bool m_vbufReady = false; + std::unique_ptr<QRhiBuffer> m_ubuf; + std::unique_ptr<QRhiShaderResourceBindings> m_srb; + std::unique_ptr<QRhiGraphicsPipeline> m_ps; + + float m_rotation = 0; + float m_opacity = 1; + int m_opacityDir = -1; +}; + +#endif diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp b/tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp new file mode 100644 index 0000000000..4ef3359ed0 --- /dev/null +++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +// This is a compact, minimal demo of deciding the backend at runtime while +// using the exact same shaders and rendering code without any branching +// whatsoever once the QWindow is up and the RHI is initialized. + +#include <QGuiApplication> +#include <QCommandLineParser> +#include "hellowindow.h" + +QString graphicsApiName(QRhi::Implementation graphicsApi) +{ + switch (graphicsApi) { + case QRhi::Null: + return QLatin1String("Null (no output)"); + case QRhi::OpenGLES2: + return QLatin1String("OpenGL 2.x"); + case QRhi::Vulkan: + return QLatin1String("Vulkan"); + case QRhi::D3D11: + return QLatin1String("Direct3D 11"); + case QRhi::Metal: + return QLatin1String("Metal"); + default: + break; + } + return QString(); +} + +int main(int argc, char **argv) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + + QRhi::Implementation graphicsApi; +#if defined(Q_OS_WIN) + graphicsApi = QRhi::D3D11; +#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) + graphicsApi = QRhi::Metal; +#elif QT_CONFIG(vulkan) + graphicsApi = QRhi::Vulkan; +#else + graphicsApi = QRhi::OpenGLES2; +#endif + + 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); + + cmdLineParser.process(app); + if (cmdLineParser.isSet(nullOption)) + graphicsApi = QRhi::Null; + if (cmdLineParser.isSet(glOption)) + graphicsApi = QRhi::OpenGLES2; + if (cmdLineParser.isSet(vkOption)) + graphicsApi = QRhi::Vulkan; + if (cmdLineParser.isSet(d3dOption)) + graphicsApi = QRhi::D3D11; + if (cmdLineParser.isSet(mtlOption)) + graphicsApi = QRhi::Metal; + + qDebug("Selected graphics API is %s", qPrintable(graphicsApiName(graphicsApi))); + qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText())); + + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + QSurfaceFormat::setDefaultFormat(fmt); + +#if QT_CONFIG(vulkan) + QVulkanInstance inst; + if (graphicsApi == QRhi::Vulkan) { +#ifndef Q_OS_ANDROID + inst.setLayers({ "VK_LAYER_LUNARG_standard_validation" }); +#else + inst.setLayers({ + "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({ "VK_KHR_get_physical_device_properties2" }); + if (!inst.create()) { + qWarning("Failed to create Vulkan instance, switching to OpenGL"); + graphicsApi = QRhi::OpenGLES2; + } + } +#endif + + HelloWindow w(graphicsApi); +#if QT_CONFIG(vulkan) + if (graphicsApi == QRhi::Vulkan) + w.setVulkanInstance(&inst); +#endif + w.resize(1280, 720); + w.setTitle(QCoreApplication::applicationName() + QLatin1String(" - ") + graphicsApiName(graphicsApi)); + w.show(); + + 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/hellominimalcrossgfxtriangle/window.cpp b/tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp new file mode 100644 index 0000000000..f3f00e0255 --- /dev/null +++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#include "window.h" +#include <QPlatformSurfaceEvent> + +Window::Window(QRhi::Implementation graphicsApi) + : m_graphicsApi(graphicsApi) +{ + switch (graphicsApi) { + case QRhi::OpenGLES2: + setSurfaceType(OpenGLSurface); +#if QT_CONFIG(opengl) + setFormat(QRhiGles2InitParams::adjustedFormat()); +#endif + break; + case QRhi::Vulkan: + setSurfaceType(VulkanSurface); + break; + case QRhi::D3D11: + setSurfaceType(OpenGLSurface); + break; + case QRhi::Metal: + setSurfaceType(MetalSurface); + break; + default: + break; + } +} + +void Window::exposeEvent(QExposeEvent *) +{ + // initialize and start rendering when the window becomes usable for graphics purposes + if (isExposed() && !m_running) { + qDebug("init"); + 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) { + qDebug("not exposed"); + m_notExposed = true; + } + + // Continue when exposed again and the surface has a valid size. Note that + // surfaceSize can be (0, 0) even though size() reports a valid one, hence + // trusting surfacePixelSize() and not QWindow. + if (isExposed() && m_running && m_notExposed && !surfaceSize.isEmpty()) { + qDebug("exposed again"); + 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() +{ + QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableProfiling; + + if (m_graphicsApi == QRhi::Null) { + QRhiNullInitParams params; + m_rhi.reset(QRhi::create(QRhi::Null, ¶ms, rhiFlags)); + } + +#if QT_CONFIG(opengl) + if (m_graphicsApi == QRhi::OpenGLES2) { + m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface()); + QRhiGles2InitParams params; + params.fallbackSurface = m_fallbackSurface.get(); + params.window = this; + m_rhi.reset(QRhi::create(QRhi::OpenGLES2, ¶ms, rhiFlags)); + } +#endif + +#if QT_CONFIG(vulkan) + if (m_graphicsApi == QRhi::Vulkan) { + QRhiVulkanInitParams params; + params.inst = vulkanInstance(); + params.window = this; + m_rhi.reset(QRhi::create(QRhi::Vulkan, ¶ms, rhiFlags)); + } +#endif + +#ifdef Q_OS_WIN + if (m_graphicsApi == QRhi::D3D11) { + QRhiD3D11InitParams params; + params.enableDebugLayer = true; + m_rhi.reset(QRhi::create(QRhi::D3D11, ¶ms, rhiFlags)); + } +#endif + +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) + if (m_graphicsApi == QRhi::Metal) { + QRhiMetalInitParams params; + m_rhi = QRhi::create(QRhi::Metal, ¶ms, rhiFlags); + } +#endif + + if (!m_rhi) + qFatal("Failed to create RHI backend"); + + m_sc.reset(m_rhi->newSwapChain()); + m_ds.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, + QSize(), // no need to set the size here, due to UsedWithSwapChainOnly + 1, + QRhiRenderBuffer::UsedWithSwapChainOnly)); + m_sc->setWindow(this); + m_sc->setDepthStencil(m_ds.get()); + m_rp.reset(m_sc->newCompatibleRenderPassDescriptor()); + m_sc->setRenderPassDescriptor(m_rp.get()); + + customInit(); +} + +void Window::resizeSwapChain() +{ + m_hasSwapChain = m_sc->buildOrResize(); // also handles m_ds + + const QSize outputSize = m_sc->currentPixelSize(); + m_proj = m_rhi->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 newly exposed, resize the swapchain. (the + // newly-exposed case is not actually required by some platforms, but + // f.ex. Vulkan on Windows seems to need it) + // + // This (exposeEvent + the logic here) is the only safe way to perform + // resize handling. Note the usage of the RHI's surfacePixelSize(), and + // never QWindow::size(). (the two may or may not be the same under the hood, + // depending on the backend and platform) + // + if (m_sc->currentPixelSize() != m_sc->surfacePixelSize() || m_newlyExposed) { + resizeSwapChain(); + if (!m_hasSwapChain) + return; + m_newlyExposed = false; + } + + QRhi::FrameOpResult r = m_rhi->beginFrame(m_sc.get()); + if (r == QRhi::FrameOpSwapChainOutOfDate) { + resizeSwapChain(); + if (!m_hasSwapChain) + return; + r = m_rhi->beginFrame(m_sc.get()); + } + if (r != QRhi::FrameOpSuccess) { + qDebug("beginFrame failed with %d, retry", r); + requestUpdate(); + return; + } + + customRender(); + + m_rhi->endFrame(m_sc.get()); + + requestUpdate(); +} + +void Window::customInit() +{ +} + +void Window::customRender() +{ +} diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/window.h b/tests/manual/rhi/hellominimalcrossgfxtriangle/window.h new file mode 100644 index 0000000000..30bfa2492c --- /dev/null +++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/window.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef WINDOW_H +#define WINDOW_H + +#include <QWindow> + +#include <QtGui/private/qrhinull_p.h> +#if QT_CONFIG(opengl) +#include <QtGui/private/qrhigles2_p.h> +#include <QOffscreenSurface> +#endif +#if QT_CONFIG(vulkan) +#include <QtGui/private/qrhivulkan_p.h> +#endif +#ifdef Q_OS_WIN +#include <QtGui/private/qrhid3d11_p.h> +#endif +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#include <QtGui/private/qrhimetal_p.h> +#endif + +class Window : public QWindow +{ +public: + Window(QRhi::Implementation graphicsApi); + + void releaseSwapChain(); + +protected: + virtual void customInit(); + virtual void customRender(); + + // destruction order matters to a certain degree: the fallbackSurface must + // outlive the rhi, the rhi must outlive all other resources. The resources + // need no special order when destroying. +#if QT_CONFIG(opengl) + std::unique_ptr<QOffscreenSurface> m_fallbackSurface; +#endif + std::unique_ptr<QRhi> m_rhi; + std::unique_ptr<QRhiSwapChain> m_sc; + std::unique_ptr<QRhiRenderBuffer> m_ds; + std::unique_ptr<QRhiRenderPassDescriptor> m_rp; + + bool m_hasSwapChain = false; + QMatrix4x4 m_proj; + +private: + void init(); + void resizeSwapChain(); + void render(); + + void exposeEvent(QExposeEvent *) override; + bool event(QEvent *) override; + + QRhi::Implementation m_graphicsApi; + + bool m_running = false; + bool m_notExposed = false; + bool m_newlyExposed = false; +}; + +#endif diff --git a/tests/manual/rhi/instancing/inst.frag.qsb b/tests/manual/rhi/instancing/inst.frag.qsb Binary files differindex f19a2f0a71..cd6b948227 100644 --- a/tests/manual/rhi/instancing/inst.frag.qsb +++ b/tests/manual/rhi/instancing/inst.frag.qsb diff --git a/tests/manual/rhi/instancing/inst.vert.qsb b/tests/manual/rhi/instancing/inst.vert.qsb Binary files differindex de05eae80f..4b891636e7 100644 --- a/tests/manual/rhi/instancing/inst.vert.qsb +++ b/tests/manual/rhi/instancing/inst.vert.qsb diff --git a/tests/manual/rhi/mrt/mrt.frag.qsb b/tests/manual/rhi/mrt/mrt.frag.qsb Binary files differindex 389b6affa3..0c83b71ee6 100644 --- a/tests/manual/rhi/mrt/mrt.frag.qsb +++ b/tests/manual/rhi/mrt/mrt.frag.qsb diff --git a/tests/manual/rhi/mrt/mrt.vert.qsb b/tests/manual/rhi/mrt/mrt.vert.qsb Binary files differindex ff8c0e50bb..ecd4a2eb71 100644 --- a/tests/manual/rhi/mrt/mrt.vert.qsb +++ b/tests/manual/rhi/mrt/mrt.vert.qsb diff --git a/tests/manual/rhi/multiwindow/multiwindow.cpp b/tests/manual/rhi/multiwindow/multiwindow.cpp index 4d5de16a58..5fb5bb22ab 100644 --- a/tests/manual/rhi/multiwindow/multiwindow.cpp +++ b/tests/manual/rhi/multiwindow/multiwindow.cpp @@ -78,7 +78,7 @@ #include <QtGui/private/qrhid3d11_p.h> #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) #include <QtGui/private/qrhimetal_p.h> #endif @@ -148,7 +148,7 @@ void createRhi() } #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) if (graphicsApi == Metal) { QRhiMetalInitParams params; r.r = QRhi::create(QRhi::Metal, ¶ms); @@ -530,7 +530,7 @@ int main(int argc, char **argv) #if defined(Q_OS_WIN) graphicsApi = D3D11; -#elif defined(Q_OS_DARWIN) +#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) graphicsApi = Metal; #elif QT_CONFIG(vulkan) graphicsApi = Vulkan; diff --git a/tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp b/tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp index 37c6cd04c3..75a1590d94 100644 --- a/tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp +++ b/tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp @@ -82,7 +82,7 @@ #include <QtGui/private/qrhid3d11_p.h> #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) #include <QtGui/private/qrhimetal_p.h> #endif @@ -376,7 +376,7 @@ void Renderer::createRhi() } #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) if (graphicsApi == Metal) { QRhiMetalInitParams params; r = QRhi::create(QRhi::Metal, ¶ms, rhiFlags); @@ -730,7 +730,7 @@ int main(int argc, char **argv) #if defined(Q_OS_WIN) graphicsApi = D3D11; -#elif defined(Q_OS_DARWIN) +#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) graphicsApi = Metal; #elif QT_CONFIG(vulkan) graphicsApi = Vulkan; diff --git a/tests/manual/rhi/offscreen/offscreen.cpp b/tests/manual/rhi/offscreen/offscreen.cpp index 79e50d3dd4..31c0632be1 100644 --- a/tests/manual/rhi/offscreen/offscreen.cpp +++ b/tests/manual/rhi/offscreen/offscreen.cpp @@ -72,7 +72,7 @@ #include <QtGui/private/qrhid3d11_p.h> #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) #include <QtGui/private/qrhimetal_p.h> #endif @@ -130,7 +130,7 @@ int main(int argc, char **argv) #if defined(Q_OS_WIN) graphicsApi = D3D11; -#elif defined(Q_OS_DARWIN) +#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) graphicsApi = Metal; #elif QT_CONFIG(vulkan) graphicsApi = Vulkan; @@ -217,7 +217,7 @@ int main(int argc, char **argv) } #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) if (graphicsApi == Metal) { QRhiMetalInitParams params; r = QRhi::create(QRhi::Metal, ¶ms); diff --git a/tests/manual/rhi/rhi.pro b/tests/manual/rhi/rhi.pro index 4768ee1c6d..4bee164969 100644 --- a/tests/manual/rhi/rhi.pro +++ b/tests/manual/rhi/rhi.pro @@ -9,11 +9,13 @@ SUBDIRS += \ msaarenderbuffer \ cubemap \ cubemap_scissor \ + cubemap_render \ multiwindow \ multiwindow_threaded \ triquadcube \ offscreen \ floattexture \ + float16texture_with_compute \ mrt \ shadowmap \ computebuffer \ diff --git a/tests/manual/rhi/shadowmap/buildshaders.sh b/tests/manual/rhi/shadowmap/buildshaders.sh deleted file mode 100755 index 8991bb074a..0000000000 --- a/tests/manual/rhi/shadowmap/buildshaders.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -qsb --glsl "120,300 es" --hlsl 50 --msl 12 shadowmap.vert -o shadowmap.vert.qsb -qsb --glsl "120,300 es" --hlsl 50 --msl 12 shadowmap.frag -o shadowmap.frag.qsb -qsb --glsl "120,300 es" --hlsl 50 --msl 12 main.vert -o main.vert.qsb -qsb --glsl "120,300 es" --hlsl 50 --msl 12 main.frag -o main.frag.qsb diff --git a/tests/manual/rhi/shadowmap/main.frag.qsb b/tests/manual/rhi/shadowmap/main.frag.qsb Binary files differindex 3001908b85..373c62f76d 100644 --- a/tests/manual/rhi/shadowmap/main.frag.qsb +++ b/tests/manual/rhi/shadowmap/main.frag.qsb diff --git a/tests/manual/rhi/shadowmap/main.vert.qsb b/tests/manual/rhi/shadowmap/main.vert.qsb Binary files differindex 8b81a93c00..6392d17cd6 100644 --- a/tests/manual/rhi/shadowmap/main.vert.qsb +++ b/tests/manual/rhi/shadowmap/main.vert.qsb diff --git a/tests/manual/rhi/shadowmap/shadowmap.frag.qsb b/tests/manual/rhi/shadowmap/shadowmap.frag.qsb Binary files differindex 3cad114cf4..fabeb32e65 100644 --- a/tests/manual/rhi/shadowmap/shadowmap.frag.qsb +++ b/tests/manual/rhi/shadowmap/shadowmap.frag.qsb diff --git a/tests/manual/rhi/shadowmap/shadowmap.vert.qsb b/tests/manual/rhi/shadowmap/shadowmap.vert.qsb Binary files differindex 37a5e6ecbf..f224728ff2 100644 --- a/tests/manual/rhi/shadowmap/shadowmap.vert.qsb +++ b/tests/manual/rhi/shadowmap/shadowmap.vert.qsb diff --git a/tests/manual/rhi/shared/color.frag.qsb b/tests/manual/rhi/shared/color.frag.qsb Binary files differindex 3a965682eb..51501ad5e6 100644 --- a/tests/manual/rhi/shared/color.frag.qsb +++ b/tests/manual/rhi/shared/color.frag.qsb diff --git a/tests/manual/rhi/shared/color.vert.qsb b/tests/manual/rhi/shared/color.vert.qsb Binary files differindex e34eae79a0..e342718fec 100644 --- a/tests/manual/rhi/shared/color.vert.qsb +++ b/tests/manual/rhi/shared/color.vert.qsb diff --git a/tests/manual/rhi/shared/examplefw.h b/tests/manual/rhi/shared/examplefw.h index d28bbea0a8..84895cf530 100644 --- a/tests/manual/rhi/shared/examplefw.h +++ b/tests/manual/rhi/shared/examplefw.h @@ -78,7 +78,7 @@ #include <QtGui/private/qrhid3d11_p.h> #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) #include <QtGui/private/qrhimetal_p.h> #endif @@ -123,9 +123,9 @@ QString graphicsApiName() QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers; int sampleCount = 1; -QRhiSwapChain::Flags scFlags = 0; -QRhi::BeginFrameFlags beginFrameFlags = 0; -QRhi::EndFrameFlags endFrameFlags = 0; +QRhiSwapChain::Flags scFlags; +QRhi::BeginFrameFlags beginFrameFlags; +QRhi::EndFrameFlags endFrameFlags; int framesUntilTdr = -1; bool transparentBackground = false; @@ -148,6 +148,9 @@ protected: void exposeEvent(QExposeEvent *) override; bool event(QEvent *) override; +#ifdef EXAMPLEFW_KEYPRESS_EVENTS + void keyPressEvent(QKeyEvent *e) override; +#endif bool m_running = false; bool m_notExposed = false; @@ -292,7 +295,7 @@ void Window::init() } #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) if (graphicsApi == Metal) { QRhiMetalInitParams params; m_r = QRhi::create(QRhi::Metal, ¶ms, rhiFlags); @@ -445,7 +448,7 @@ int main(int argc, char **argv) // Defaults. #if defined(Q_OS_WIN) graphicsApi = D3D11; -#elif defined(Q_OS_DARWIN) +#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) graphicsApi = Metal; #elif QT_CONFIG(vulkan) graphicsApi = Vulkan; diff --git a/tests/manual/rhi/shared/texture.frag.qsb b/tests/manual/rhi/shared/texture.frag.qsb Binary files differindex 31bcd7105e..ad11300a59 100644 --- a/tests/manual/rhi/shared/texture.frag.qsb +++ b/tests/manual/rhi/shared/texture.frag.qsb diff --git a/tests/manual/rhi/shared/texture.vert.qsb b/tests/manual/rhi/shared/texture.vert.qsb Binary files differindex 1b9e52890d..288cce8176 100644 --- a/tests/manual/rhi/shared/texture.vert.qsb +++ b/tests/manual/rhi/shared/texture.vert.qsb diff --git a/tests/manual/rhi/shared/texture_ms4.frag.qsb b/tests/manual/rhi/shared/texture_ms4.frag.qsb Binary files differindex 7f187ce7f0..278313111b 100644 --- a/tests/manual/rhi/shared/texture_ms4.frag.qsb +++ b/tests/manual/rhi/shared/texture_ms4.frag.qsb diff --git a/tests/manual/rhi/texuploads/texuploads.cpp b/tests/manual/rhi/texuploads/texuploads.cpp index 4c10a6b965..a6b7d87d3e 100644 --- a/tests/manual/rhi/texuploads/texuploads.cpp +++ b/tests/manual/rhi/texuploads/texuploads.cpp @@ -237,11 +237,11 @@ void Window::customRender() // Exercise texture object export/import. if (d.testStage == 6) { - const QRhiNativeHandles *h = d.tex->nativeHandles(); - if (h) { -#ifdef Q_OS_DARWIN + const QRhiTexture::NativeTexture nativeTexture = d.tex->nativeTexture(); + if (nativeTexture.object) { +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) if (graphicsApi == Metal) { - qDebug() << "Metal texture: " << static_cast<const QRhiMetalTextureNativeHandles *>(h)->texture; + qDebug() << "Metal texture: " << *(void**)nativeTexture.object; // Now could cast to id<MTLTexture> and do something with // it, keeping in mind that copy operations are only done // in beginPass, while rendering into a texture may only @@ -253,7 +253,7 @@ void Window::customRender() d.importedTex = m_r->newTexture(QRhiTexture::RGBA8, d.tex->pixelSize()); d.releasePool << d.importedTex; - if (!d.importedTex->buildFrom(h)) + if (!d.importedTex->buildFrom(nativeTexture)) qWarning("Texture import failed"); // now d.tex and d.importedTex use the same MTLTexture diff --git a/tests/manual/rhi/triquadcube/texturedcuberenderer.cpp b/tests/manual/rhi/triquadcube/texturedcuberenderer.cpp index 8c5845d4fc..d8dea6d91f 100644 --- a/tests/manual/rhi/triquadcube/texturedcuberenderer.cpp +++ b/tests/manual/rhi/triquadcube/texturedcuberenderer.cpp @@ -78,7 +78,7 @@ void TexturedCubeRenderer::initResources(QRhiRenderPassDescriptor *rp) m_ubuf->build(); m_image = QImage(QLatin1String(":/qt256.png")).convertToFormat(QImage::Format_RGBA8888); - QRhiTexture::Flags texFlags = 0; + QRhiTexture::Flags texFlags; if (MIPMAP) texFlags |= QRhiTexture::MipMapped; if (AUTOGENMIPMAP) diff --git a/tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp b/tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp index 250ae3a2ee..3a58eed6ce 100644 --- a/tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp +++ b/tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp @@ -158,7 +158,7 @@ void TriangleOnCubeRenderer::initResources(QRhiRenderPassDescriptor *rp) m_ps->build(); - QRhiTextureRenderTarget::Flags rtFlags = 0; + QRhiTextureRenderTarget::Flags rtFlags; if (IMAGE_UNDER_OFFSCREEN_RENDERING) rtFlags |= QRhiTextureRenderTarget::PreserveColorContents; diff --git a/tests/manual/startsystemmove/main.cpp b/tests/manual/startsystemmove/main.cpp new file mode 100644 index 0000000000..a121d1ed86 --- /dev/null +++ b/tests/manual/startsystemmove/main.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +constexpr qreal border = 20; + +class Window : public QRasterWindow +{ +public: + explicit Window(QWindow *parent = nullptr) : QRasterWindow(parent) + { + resize(300, 200); + setMinimumSize(QSize(border*2, border*2)); + } +protected: + void resizeOrMove(const QPointF &p); + bool event(QEvent *event) override; + void paintEvent(QPaintEvent *event) override; +}; + +void Window::resizeOrMove(const QPointF &p) +{ + Qt::Edges edges; + if (p.x() > width() - border) + edges |= Qt::RightEdge; + if (p.x() < border) + edges |= Qt::LeftEdge; + if (p.y() < border) + edges |= Qt::TopEdge; + if (p.y() > height() - border) + edges |= Qt::BottomEdge; + + if (edges != 0) { + qDebug() << "startSystemResize" << edges; + if (startSystemResize(edges)) + qDebug() << " -> supported"; + else + qDebug() << " -> not supported"; + } else { + qDebug() << "startSystemMove"; + if (startSystemMove()) + qDebug() << " -> supported"; + else + qDebug() << " -> not supported"; + } +} + +bool Window::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::MouseButtonPress: + qDebug() << "Mouse press"; + resizeOrMove(static_cast<QMouseEvent *>(event)->localPos()); + return true; + case QEvent::TouchUpdate: + qDebug() << "Touch update"; + resizeOrMove(static_cast<QTouchEvent *>(event)->touchPoints().first().pos()); + return true; + case QEvent::TouchBegin: + qDebug() << "Touch begin"; + resizeOrMove(static_cast<QTouchEvent *>(event)->touchPoints().first().pos()); + return true; + default: + return QRasterWindow::event(event); + } +} + +void Window::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainter painter(this); + QRect fullRect(0, 0, width(), height()); + QRect innerRect = fullRect.marginsRemoved(QMargins(border, border, border, border)); + painter.fillRect(fullRect, QGradient::WarmFlame); + painter.fillRect(innerRect, QGradient::NightFade); + painter.drawText(QRectF(0, 0, width(), height()), Qt::AlignCenter, QStringLiteral("Click mouse or touch to move window\nDrag along the sides to resize.")); +} + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + Window window; + window.show(); + return app.exec(); +} diff --git a/tests/manual/startsystemmove/startsystemmove.pro b/tests/manual/startsystemmove/startsystemmove.pro new file mode 100644 index 0000000000..568bda343b --- /dev/null +++ b/tests/manual/startsystemmove/startsystemmove.pro @@ -0,0 +1,4 @@ +TEMPLATE = app +QT = core gui +SOURCES += main.cpp +CONFIG += console diff --git a/tests/manual/widgets/widgets/qtabbar/qtabbar.pro b/tests/manual/widgets/widgets/qtabbar/qtabbar.pro new file mode 100644 index 0000000000..b39c81493b --- /dev/null +++ b/tests/manual/widgets/widgets/qtabbar/qtabbar.pro @@ -0,0 +1 @@ +SUBDIRS = stylesheet diff --git a/tests/manual/widgets/widgets/qtabbar/stylesheet/main.cpp b/tests/manual/widgets/widgets/qtabbar/stylesheet/main.cpp new file mode 100644 index 0000000000..02393f66f9 --- /dev/null +++ b/tests/manual/widgets/widgets/qtabbar/stylesheet/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** + ** + ** Copyright (C) 2020 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the test suite 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$ + ** + ****************************************************************************/ + +// This test is for checking that when there is padding set on the stylesheet and the elide mode is +// set that it is correctly shown as elided and not clipped. + +#include <QApplication> +#include <QTabBar> +#include <QIcon> + +int main(int argc, char** argv) +{ + QApplication app(argc, argv); + app.setStyleSheet("QTabBar::tab { padding-left: 20px; }\n"); + QIcon icon(":/v.ico"); + + QTabBar b; + b.setElideMode(Qt::ElideRight); + b.addTab(icon, "some text"); + b.resize(80,32); + b.show(); + + return app.exec(); +} diff --git a/tests/manual/widgets/widgets/qtabbar/stylesheet/res.qrc b/tests/manual/widgets/widgets/qtabbar/stylesheet/res.qrc new file mode 100644 index 0000000000..9d0bb8e061 --- /dev/null +++ b/tests/manual/widgets/widgets/qtabbar/stylesheet/res.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>v.ico</file> + </qresource> +</RCC> diff --git a/tests/manual/widgets/widgets/qtabbar/stylesheet/stylesheet.pro b/tests/manual/widgets/widgets/qtabbar/stylesheet/stylesheet.pro new file mode 100644 index 0000000000..4957503abc --- /dev/null +++ b/tests/manual/widgets/widgets/qtabbar/stylesheet/stylesheet.pro @@ -0,0 +1,5 @@ +QT += widgets +TEMPLATE = app +TARGET = stylesheet +RESOURCES += res.qrc +SOURCES += main.cpp diff --git a/tests/manual/widgets/widgets/qtabbar/stylesheet/v.ico b/tests/manual/widgets/widgets/qtabbar/stylesheet/v.ico Binary files differnew file mode 100644 index 0000000000..90dfbc9f9b --- /dev/null +++ b/tests/manual/widgets/widgets/qtabbar/stylesheet/v.ico diff --git a/tests/manual/widgets/widgets/widgets.pro b/tests/manual/widgets/widgets/widgets.pro index 1fccb09d79..5a07e621e8 100644 --- a/tests/manual/widgets/widgets/widgets.pro +++ b/tests/manual/widgets/widgets/widgets.pro @@ -2,4 +2,5 @@ TEMPLATE = subdirs SUBDIRS = bigmenucreator \ defaultUpMenuBar \ multiscreen-menus \ - qtoolbutton/menuOnMultiScreens + qtoolbutton/menuOnMultiScreens \ + qtabbar diff --git a/tests/manual/windowflags/controls.cpp b/tests/manual/windowflags/controls.cpp index c167e5f8bf..b999a59abe 100644 --- a/tests/manual/windowflags/controls.cpp +++ b/tests/manual/windowflags/controls.cpp @@ -95,7 +95,7 @@ HintControl::HintControl(QWidget *parent) Qt::WindowFlags HintControl::hints() const { - Qt::WindowFlags flags = 0; + Qt::WindowFlags flags; if (msWindowsFixedSizeDialogCheckBox->isChecked()) flags |= Qt::MSWindowsFixedSizeDialogHint; if (x11BypassWindowManagerCheckBox->isChecked()) |