From f9d90c6fbab1d4f66dcc3fbc06f7f157693d04d1 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 19 Jun 2023 11:56:48 +0200 Subject: [PATCH] rhi: Introduce multiview starting with OpenGL (ES) Fixes: QTBUG-114770 Change-Id: Ibb1ced7f19d15a5116c60e95fd3e6b86ace63155 Reviewed-by: Andy Nichols --- src/gui/opengl/qopenglextensions_p.h | 4 +- src/gui/opengl/qopenglfunctions.cpp | 4 + src/gui/rhi/qrhi.cpp | 80 ++++++ src/gui/rhi/qrhi.h | 7 +- src/gui/rhi/qrhid3d11.cpp | 2 + src/gui/rhi/qrhid3d12.cpp | 2 + src/gui/rhi/qrhigles2.cpp | 20 +- src/gui/rhi/qrhigles2_p.h | 6 +- src/gui/rhi/qrhimetal.mm | 2 + src/gui/rhi/qrhivulkan.cpp | 2 + tests/manual/rhi/CMakeLists.txt | 1 + tests/manual/rhi/multiview/CMakeLists.txt | 21 ++ tests/manual/rhi/multiview/buildshaders.bat | 4 + tests/manual/rhi/multiview/multiview.cpp | 239 ++++++++++++++++++ tests/manual/rhi/multiview/multiview.frag | 10 + tests/manual/rhi/multiview/multiview.frag.qsb | Bin 0 -> 577 bytes tests/manual/rhi/multiview/multiview.pro | 8 + tests/manual/rhi/multiview/multiview.qrc | 8 + tests/manual/rhi/multiview/multiview.vert | 18 ++ tests/manual/rhi/multiview/multiview.vert.qsb | Bin 0 -> 1028 bytes tests/manual/rhi/multiview/texture.frag | 19 ++ tests/manual/rhi/multiview/texture.frag.qsb | Bin 0 -> 1123 bytes tests/manual/rhi/multiview/texture.vert | 20 ++ tests/manual/rhi/multiview/texture.vert.qsb | Bin 0 -> 1154 bytes tests/manual/rhi/rhi.pro | 12 +- 25 files changed, 483 insertions(+), 6 deletions(-) create mode 100644 tests/manual/rhi/multiview/CMakeLists.txt create mode 100644 tests/manual/rhi/multiview/buildshaders.bat create mode 100644 tests/manual/rhi/multiview/multiview.cpp create mode 100644 tests/manual/rhi/multiview/multiview.frag create mode 100644 tests/manual/rhi/multiview/multiview.frag.qsb create mode 100644 tests/manual/rhi/multiview/multiview.pro create mode 100644 tests/manual/rhi/multiview/multiview.qrc create mode 100644 tests/manual/rhi/multiview/multiview.vert create mode 100644 tests/manual/rhi/multiview/multiview.vert.qsb create mode 100644 tests/manual/rhi/multiview/texture.frag create mode 100644 tests/manual/rhi/multiview/texture.frag.qsb create mode 100644 tests/manual/rhi/multiview/texture.vert create mode 100644 tests/manual/rhi/multiview/texture.vert.qsb diff --git a/src/gui/opengl/qopenglextensions_p.h b/src/gui/opengl/qopenglextensions_p.h index fdb9b51f067..58231545c6b 100644 --- a/src/gui/opengl/qopenglextensions_p.h +++ b/src/gui/opengl/qopenglextensions_p.h @@ -59,7 +59,9 @@ public: StandardDerivatives = 0x02000000, ASTCTextureCompression = 0x04000000, ETC2TextureCompression = 0x08000000, - HalfFloatVertex = 0x10000000 + HalfFloatVertex = 0x10000000, + MultiView = 0x20000000, + MultiViewExtended = 0x40000000 }; Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension) diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp index ee769875667..0b760eb2dd1 100644 --- a/src/gui/opengl/qopenglfunctions.cpp +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -348,6 +348,10 @@ static int qt_gl_resolve_extensions() extensions |= QOpenGLExtensions::StandardDerivatives; if (extensionMatcher.match("GL_ARB_half_float_vertex")) extensions |= QOpenGLExtensions::HalfFloatVertex; + if (extensionMatcher.match("GL_OVR_multiview")) + extensions |= QOpenGLExtensions::MultiView; + if (extensionMatcher.match("GL_OVR_multiview2")) + extensions |= QOpenGLExtensions::MultiViewExtended; if (ctx->isOpenGLES()) { if (format.majorVersion() >= 2) diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 16c508b36c0..6b3a4bd7c3f 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -967,6 +967,22 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") \value ThreeDimensionalTextureMipmaps Indicates that generating 3D texture mipmaps are supported. In practice this feature will be unsupported with Direct 3D 12. + + \value MultiView Indicates that multiview, see e.g. + \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_multiview.html}{VK_KHR_multiview} + is supported. With OpenGL ES 2.0, Direct 3D 11, and OpenGL (ES) + implementations without \c{GL_OVR_multiview2} this feature will not be + supported. With Vulkan 1.1 and newer, and Direct 3D 12 multiview is + typically supported. When reported as supported, creating a + QRhiTextureRenderTarget with a QRhiColorAttachment that references a texture + array and has \l{QRhiColorAttachment::setMultiViewCount()}{multiViewCount} + set enables recording a render pass that uses multiview rendering. Note that + multiview is only available in combination with 2D texture arrays. It cannot + be used to optimize the rendering into individual textures (e.g. two, for + the left and right eyes). Rather, the target of a multiview render pass is + always a texture array, automatically rendering to the layer (array element) + corresponding to each view. Therefore this feature implies \l TextureArrays + as well. This enum value has been introduced in Qt 6.7. */ /*! @@ -2296,6 +2312,70 @@ QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer) Sets the resolve texture mip \a level to use. */ +/*! + \fn int QRhiColorAttachment::multiViewCount() const + + \return the currently set number of views. Defaults to 0 which indicates + the render target with this color attachment is not going to be used with + multiview rendering. + + \since 6.7 + */ + +/*! + \fn void QRhiColorAttachment::setMultiViewCount(int count) + + Sets the view \a count. Setting a value larger than 1 indicates that the + render target with this color attachment is going to be used with multiview + rendering. The default value is 0. Values smaller than 2 indicate no + multiview rendering. + + When \a count is set to \c 2 or greater, the color attachment must be + associated with a 2D texture array. layer() and multiViewCount() together + define the range of texture array elements that are targeted during + multiview rendering. + + For example, if \c layer is \c 0 and \c multiViewCount is \c 2, the texture + array must have 2 (or more) elements, and the multiview rendering will + target elements 0 and 1. The \c{gl_ViewIndex} variable in the shaders has a + value of \c 0 or \c 1 then, where view \c 0 corresponds to the texture array + element \c 0, and view \c 1 to the array element \c 1. + + \note Setting a \a count larger than 1, using a texture array as texture(), + and calling \l{QRhiCommandBuffer::beginPass()}{beginPass()} on a + QRhiTextureRenderTarget with this color attachment implies multiview + rendering for the entire render pass. multiViewCount() should not be set + unless multiview rendering is wanted. Multiview cannot be used with texture + types other than 2D texture arrays. (although 3D textures may work, + depending on the graphics API and backend; applications are nonetheless + advised not to rely on that and only use 2D texture arrays as the render + targets of multiview rendering) + + See + \l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview} + for more details regarding multiview rendering. Do note that Qt requires + \l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview2.txt}{GL_OVR_multiview2} + as well, when running on OpenGL (ES). + + Multiview rendering is available only when the + \l{QRhi::MultiView}{MultiView} feature is reported as supported from + \l{QRhi::isFeatureSupported()}{isFeatureSupported()}. + + \note For portability, be aware of limitations that exist for multiview + rendering with some of the graphics APIs. For example, OpenGL disallows + tessellation or geometry shaders with multiview. With other APIs, e.g. + Vulkan, some of these are optional features, the actual support depending + on the implementation. It is therefore recommended that multiview render + passes do not rely on any of the features that + \l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview} + declares as unsupported. The one exception is shader stage outputs other + than \c{gl_Position} depending on \c{gl_ViewIndex}: that can be relied on + (even with OpenGL) because QRhi never reports multiview as supported without + \c{GL_OVR_multiview2} also being present. + + \since 6.7 + */ + /*! \class QRhiTextureRenderTargetDescription \inmodule QtGui diff --git a/src/gui/rhi/qrhi.h b/src/gui/rhi/qrhi.h index eb300f5873b..585bead7b39 100644 --- a/src/gui/rhi/qrhi.h +++ b/src/gui/rhi/qrhi.h @@ -592,6 +592,9 @@ public: int resolveLevel() const { return m_resolveLevel; } void setResolveLevel(int level) { m_resolveLevel = level; } + int multiViewCount() const { return m_multiViewCount; } + void setMultiViewCount(int count) { m_multiViewCount = count; } + private: QRhiTexture *m_texture = nullptr; QRhiRenderBuffer *m_renderBuffer = nullptr; @@ -600,6 +603,7 @@ private: QRhiTexture *m_resolveTexture = nullptr; int m_resolveLayer = 0; int m_resolveLevel = 0; + int m_multiViewCount = 0; }; Q_DECLARE_TYPEINFO(QRhiColorAttachment, Q_RELOCATABLE_TYPE); @@ -1822,7 +1826,8 @@ public: OneDimensionalTextureMipmaps, HalfAttributes, RenderToOneDimensionalTexture, - ThreeDimensionalTextureMipmaps + ThreeDimensionalTextureMipmaps, + MultiView }; enum BeginFrameFlag { diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index feda195c516..f8284462eed 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -588,6 +588,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ThreeDimensionalTextureMipmaps: return true; + case QRhi::MultiView: + return false; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp index 7c55ff9f5ce..55e2d626e02 100644 --- a/src/gui/rhi/qrhid3d12.cpp +++ b/src/gui/rhi/qrhid3d12.cpp @@ -638,6 +638,8 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ThreeDimensionalTextureMipmaps: return false; // we generate mipmaps ourselves with compute and this is not implemented + case QRhi::MultiView: + return false; } return false; } diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 0a4bc8d01cd..af5f07d6deb 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -1009,6 +1009,15 @@ bool QRhiGles2::create(QRhi::Flags flags) caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex); + // We always require GL_OVR_multiview2 for symmetry with other backends. + caps.multiView = f->hasOpenGLExtension(QOpenGLExtensions::MultiView) + && f->hasOpenGLExtension(QOpenGLExtensions::MultiViewExtended); + if (caps.multiView) { + glFramebufferTextureMultiviewOVR = + reinterpret_cast( + ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultiviewOVR"))); + } + nativeHandlesStruct.context = ctx; contextLost = false; @@ -1371,6 +1380,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return caps.texture1D; case QRhi::ThreeDimensionalTextureMipmaps: return caps.texture3D; + case QRhi::MultiView: + return caps.multiView && caps.maxTextureArraySize > 0; default: Q_UNREACHABLE_RETURN(false); } @@ -5554,8 +5565,13 @@ bool QGles2TextureRenderTarget::create() QGles2Texture *texD = QRHI_RES(QGles2Texture, texture); Q_ASSERT(texD->texture && texD->specified); if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) { - rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture, - colorAtt.level(), colorAtt.layer()); + if (it->multiViewCount() < 2) { + rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture, + colorAtt.level(), colorAtt.layer()); + } else { + rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture, + colorAtt.level(), colorAtt.layer(), colorAtt.multiViewCount()); + } } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) { rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->target + uint(colorAtt.layer()), texD->texture, diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h index 76779fdca8e..fecb15698f8 100644 --- a/src/gui/rhi/qrhigles2_p.h +++ b/src/gui/rhi/qrhigles2_p.h @@ -909,6 +909,8 @@ public: GLsizei, const GLvoid *) = nullptr; void(QOPENGLF_APIENTRYP glFramebufferTexture1D)(GLenum, GLenum, GLenum, GLuint, GLint) = nullptr; + void(QOPENGLF_APIENTRYP glFramebufferTextureMultiviewOVR)(GLenum, GLenum, GLuint, GLint, + GLint, GLsizei) = nullptr; uint vao = 0; struct Caps { @@ -962,7 +964,8 @@ public: geometryShader(false), texture1D(false), hasDrawBuffersFunc(false), - halfAttributes(false) + halfAttributes(false), + multiView(false) { } int ctxMajor; int ctxMinor; @@ -1016,6 +1019,7 @@ public: uint texture1D : 1; uint hasDrawBuffersFunc : 1; uint halfAttributes : 1; + uint multiView : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QSet supportedCompressedFormats; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 699e7a09bea..e47ee9118a8 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -833,6 +833,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return false; case QRhi::ThreeDimensionalTextureMipmaps: return true; + case QRhi::MultiView: + return false; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 4b7af8a89ec..ae0e7051065 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -4437,6 +4437,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ThreeDimensionalTextureMipmaps: return true; + case QRhi::MultiView: + return false; default: Q_UNREACHABLE_RETURN(false); } diff --git a/tests/manual/rhi/CMakeLists.txt b/tests/manual/rhi/CMakeLists.txt index ab4093e9a6d..9fbb924f77a 100644 --- a/tests/manual/rhi/CMakeLists.txt +++ b/tests/manual/rhi/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory(stereo) add_subdirectory(tex1d) add_subdirectory(displacement) add_subdirectory(imguirenderer) +add_subdirectory(multiview) if(QT_FEATURE_widgets) add_subdirectory(rhiwidget) endif() diff --git a/tests/manual/rhi/multiview/CMakeLists.txt b/tests/manual/rhi/multiview/CMakeLists.txt new file mode 100644 index 00000000000..1e2efa02cb9 --- /dev/null +++ b/tests/manual/rhi/multiview/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_manual_test(multiview + GUI + SOURCES + multiview.cpp + LIBRARIES + Qt::Gui + Qt::GuiPrivate +) + +qt_internal_add_resource(multiview "multiview" + PREFIX + "/" + FILES + "multiview.vert.qsb" + "multiview.frag.qsb" + "texture.vert.qsb" + "texture.frag.qsb" +) diff --git a/tests/manual/rhi/multiview/buildshaders.bat b/tests/manual/rhi/multiview/buildshaders.bat new file mode 100644 index 00000000000..50ef1940585 --- /dev/null +++ b/tests/manual/rhi/multiview/buildshaders.bat @@ -0,0 +1,4 @@ +qsb --view-count 2 --glsl "300 es,330" --hlsl 61 multiview.vert -o multiview.vert.qsb +qsb --glsl "300 es,330" --hlsl 61 multiview.frag -o multiview.frag.qsb +qsb --glsl "300 es,330" --hlsl 61 texture.vert -o texture.vert.qsb +qsb --glsl "300 es,330" --hlsl 61 texture.frag -o texture.frag.qsb diff --git a/tests/manual/rhi/multiview/multiview.cpp b/tests/manual/rhi/multiview/multiview.cpp new file mode 100644 index 00000000000..f4a5db3911e --- /dev/null +++ b/tests/manual/rhi/multiview/multiview.cpp @@ -0,0 +1,239 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "../shared/examplefw.h" + +// Multiview rendering. Renders the same geometry (a triangle) with two +// different transforms into two layers of a texture array object in a *single* +// draw call. (NB under the hood it is at the hardware/driver's discretion what +// happens; it may very well map to some simple looping and still drawing +// twice, whereas with modern hardware it can be expected to be implemented +// more efficiently, but that's hidden from us) + +static float quadVertexData[] = +{ // 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 quadIndexData[] = +{ + 0, 1, 2, 0, 2, 3 +}; + +static float triangleData[] = +{ // Y up, 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, +}; + +struct { + QList releasePool; + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ibuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiTextureRenderTarget *rt = nullptr; + QRhiRenderPassDescriptor *rtRp = nullptr; + QRhiSampler *sampler = nullptr; + QRhiGraphicsPipeline *ps = nullptr; + QRhiResourceUpdateBatch *initialUpdates = nullptr; + QMatrix4x4 winProj; + QRhiTexture *tex = nullptr; + QRhiShaderResourceBindings *srb[2] = {}; + + QRhiBuffer *triUbuf = nullptr; + QRhiShaderResourceBindings *triSrb = nullptr; + QRhiGraphicsPipeline *triPs = nullptr; + QMatrix4x4 triBaseMvp; +} d; + +void Window::customInit() +{ + if (!m_r->isFeatureSupported(QRhi::MultiView)) + qFatal("Multiview is not supported"); + + // texture array with 2 elements, e.g. 0 is left eye, 1 is right + d.tex = m_r->newTextureArray(QRhiTexture::RGBA8, 2, QSize(512, 512), 1, QRhiTexture::RenderTarget); + d.releasePool << d.tex; + d.tex->create(); + + // set up the multiview render target + QRhiColorAttachment multiViewAtt(d.tex); + // using array elements 0 and 1 + multiViewAtt.setLayer(0); + multiViewAtt.setMultiViewCount(2); + QRhiTextureRenderTargetDescription rtDesc(multiViewAtt); + d.rt = m_r->newTextureRenderTarget(rtDesc); + d.releasePool << d.rt; + d.rtRp = d.rt->newCompatibleRenderPassDescriptor(); + d.releasePool << d.rtRp; + d.rt->setRenderPassDescriptor(d.rtRp); + d.rt->create(); + + // vertex buffer used by both passes + d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVertexData) + sizeof(triangleData)); + d.vbuf->create(); + d.releasePool << d.vbuf; + + // resources for the on-screen visualizer + d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(quadIndexData)); + d.ibuf->create(); + d.releasePool << d.ibuf; + + const int oneRoundedUniformBlockSize = m_r->ubufAligned(72); + d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, oneRoundedUniformBlockSize * 2); + d.ubuf->create(); + d.releasePool << d.ubuf; + + d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge); + d.releasePool << d.sampler; + d.sampler->create(); + + // two srbs, just for the quad positioning on-screen + for (int i = 0; i < 2; ++i) { + QRhiShaderResourceBindings *srb = m_r->newShaderResourceBindings(); + d.releasePool << srb; + srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, + d.ubuf, i * oneRoundedUniformBlockSize, 72), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler) + }); + srb->create(); + d.srb[i] = srb; + } + + 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, quint32(2 * sizeof(float)) } + }); + d.ps->setVertexInputLayout(inputLayout); + d.ps->setShaderResourceBindings(d.srb[0]); // all of them are layout-compatible + d.ps->setRenderPassDescriptor(m_rp); + d.ps->create(); + + d.initialUpdates = m_r->nextResourceUpdateBatch(); + d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(quadVertexData), quadVertexData); + d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(quadVertexData), sizeof(triangleData), triangleData); + d.initialUpdates->uploadStaticBuffer(d.ibuf, quadIndexData); + + qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0; + for (int i = 0; i < 2; ++i) { + d.initialUpdates->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize + 64, 4, &flip); + float layer = i; + d.initialUpdates->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize + 68, 4, &layer); + } + + // create resources for the multiview render pass + d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 128); // mat4 mvp[2] + d.releasePool << d.triUbuf; + d.triUbuf->create(); + + d.triSrb = m_r->newShaderResourceBindings(); + d.releasePool << d.triSrb; + d.triSrb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf) + }); + d.triSrb->create(); + + d.triPs = m_r->newGraphicsPipeline(); + d.releasePool << d.triPs; + d.triPs->setShaderStages({ + { QRhiShaderStage::Vertex, getShader(QLatin1String(":/multiview.vert.qsb")) }, + { QRhiShaderStage::Fragment, getShader(QLatin1String(":/multiview.frag.qsb")) } + }); + + inputLayout.setBindings({ + { 5 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float3, quint32(2 * sizeof(float)) } + }); + d.triPs->setVertexInputLayout(inputLayout); + d.triPs->setShaderResourceBindings(d.triSrb); + d.triPs->setRenderPassDescriptor(d.rtRp); + d.triPs->create(); + + d.triBaseMvp = m_r->clipSpaceCorrMatrix(); + d.triBaseMvp.perspective(45.0f, d.rt->pixelSize().width() / float(d.rt->pixelSize().height()), 0.01f, 1000.0f); + d.triBaseMvp.translate(0, 0, -2); +} + +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; + } + + QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, quint32(sizeof(quadVertexData))); + + QMatrix4x4 triMvp = d.triBaseMvp; + // let's say this is the left eye, make the triangle point left for now + triMvp.rotate(90, 0, 0, 1); + u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData()); + triMvp = d.triBaseMvp; + // right for the right eye + triMvp.rotate(270, 0, 0, 1); + u->updateDynamicBuffer(d.triUbuf, 64, 64, triMvp.constData()); + + cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 }, u); + cb->setGraphicsPipeline(d.triPs); + cb->setViewport({ 0, 0, float(d.rt->pixelSize().width()), float(d.rt->pixelSize().height()) }); + cb->setShaderResources(); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(3); + cb->endPass(); + + cb->resourceUpdate(u); + + // "blit" the two texture layers on-screen just to visualize the contents + u = m_r->nextResourceUpdateBatch(); + if (d.winProj != m_proj) { + d.winProj = m_proj; + const int oneRoundedUniformBlockSize = m_r->ubufAligned(72); + for (int i = 0; i < 2; ++i) { + QMatrix4x4 mvp = m_proj; + mvp.translate(0, 0, 1); + if (i == 0) + mvp.translate(-1.0f, 0, 0); + else + mvp.translate(1.0f, 0, 0); + u->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize, 64, mvp.constData()); + } + } + const QSize outputSizeInPixels = m_sc->currentPixelSize(); + cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u); + cb->setGraphicsPipeline(d.ps); + cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) }); + vbufBinding.second = 0; + cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16); + for (int i = 0; i < 2; ++i) { + cb->setShaderResources(d.srb[i]); + cb->drawIndexed(6); + } + cb->endPass(); +} diff --git a/tests/manual/rhi/multiview/multiview.frag b/tests/manual/rhi/multiview/multiview.frag new file mode 100644 index 00000000000..375587662f3 --- /dev/null +++ b/tests/manual/rhi/multiview/multiview.frag @@ -0,0 +1,10 @@ +#version 440 + +layout(location = 0) in vec3 v_color; + +layout(location = 0) out vec4 fragColor; + +void main() +{ + fragColor = vec4(v_color, 1.0); +} diff --git a/tests/manual/rhi/multiview/multiview.frag.qsb b/tests/manual/rhi/multiview/multiview.frag.qsb new file mode 100644 index 0000000000000000000000000000000000000000..a6dd95c720b96064d871591f7e7f55caba4f49b9 GIT binary patch literal 577 zcmV-H0>1qK00r)NoW)b$YZE~bp8ROiY-&wg&^KA>OM)ewq?TfcK17j9A+;&Rx8t~6 z(t~&x?luP^75^c@f5QLC7txvBNwc{`eDJ}6$?kqL^Ub%j7XYXMKpDM&z6%++;6n^k z^aPO-I{adT4Mt`AFvesLCeXJ^%YQ5VS1a>6vebMl6%VVVO29T8FuDgtt+Ngly}mgg zp0`sqY9BvyD86g1sblw{s)@-zcwt01jc5nGfq8^ii34|pKjI#A-m%UH*t3Z1W2_-| z+CRWu1y_mw5c7$r!o4K^Zi5*(;axu07^8f_`6bS;vu45hb=FDP6FgV+-N?SYJb^8( zVKFq~_<_h4u07_y$Mx?Jt>{@83@og{Si!>f7$g1*R8RW%xxT@1((wYj);RVP8BR3| zlzGm0C>$|0+1JA?NYXHlWXEx2kcwFn_@M@F!pY4{j;FDwx@&M4AtT1u z&E{$$XC+@gmshXf_XmT^R}M+jp~Bs9;?N*wD}v$NFHBY^1$%k>!I>UJ|Hz66FYP PbDv)Rk(c%xwdyA9ATA%I literal 0 HcmV?d00001 diff --git a/tests/manual/rhi/multiview/multiview.pro b/tests/manual/rhi/multiview/multiview.pro new file mode 100644 index 00000000000..a9606f88989 --- /dev/null +++ b/tests/manual/rhi/multiview/multiview.pro @@ -0,0 +1,8 @@ +TEMPLATE = app + +QT += gui-private + +SOURCES = \ + multiview.cpp + +RESOURCES = multiview.qrc diff --git a/tests/manual/rhi/multiview/multiview.qrc b/tests/manual/rhi/multiview/multiview.qrc new file mode 100644 index 00000000000..e931dd8c11a --- /dev/null +++ b/tests/manual/rhi/multiview/multiview.qrc @@ -0,0 +1,8 @@ + + + multiview.vert.qsb + multiview.frag.qsb + texture.vert.qsb + texture.frag.qsb + + diff --git a/tests/manual/rhi/multiview/multiview.vert b/tests/manual/rhi/multiview/multiview.vert new file mode 100644 index 00000000000..b9c9e5a704b --- /dev/null +++ b/tests/manual/rhi/multiview/multiview.vert @@ -0,0 +1,18 @@ +#version 440 +#extension GL_EXT_multiview : require + +layout(location = 0) in vec4 pos; +layout(location = 1) in vec3 color; + +layout(location = 0) out vec3 v_color; + +layout(std140, binding = 0) uniform buf +{ + mat4 mvp[2]; +}; + +void main() +{ + v_color = color; + gl_Position = mvp[gl_ViewIndex] * pos; +} diff --git a/tests/manual/rhi/multiview/multiview.vert.qsb b/tests/manual/rhi/multiview/multiview.vert.qsb new file mode 100644 index 0000000000000000000000000000000000000000..7eaa5ec4b518c204db839b10322047a629f32909 GIT binary patch literal 1028 zcmV+f1pE5{01KaZob6U?ZyQAv9Y2ybTiV2JnkMAYAug@cMwaWO6k!>G+NupiN|aP4 z5{g#NW*sfqyJjD55Q@~lz`x}Sf_rDquE&o8{mw|^oqO+@d(S;HyRr~sPUw-RX$eOJ zqDwO*t3WgN7lxQJW<{T%5v_`B{u4Au^OhJ0d!oX>ZkrWnA`u<6vPE4yr`7ujOplWH zX>|kM_lV?SFHB4SUk4>Bov03mC3zIbp6A0YMN!C?uQAjT*F^>167k95`9p~XpB2Z4 zE&Ka7M|Lmi#@@hl->EKU74G%^@qRUm&#Sx7YC;hxWCZ4EcUrvH_IzH?L4i9o*J+kH z2O-$M0S;{6fX#W{h#BIeN2*@x%fV;j^6(W@u0rPHyvu^mLWpk^z9?3ZZwC8QyKK}+ zeI%ATCC<5G?N5PQL2b5I;p6xk>MkPA_BwnV zzn|%@?M=jP0>6p-km4VrFOGjfdgR6M1!Mmo>MkGm7IQoWWhxegxc~)XA6n6H&x_Nk(p4WZ#Y4 zv+}?VBQNmfPOT>0$SS*+vFj_(>wWv6Wqb|l@+%p-za(DhTHW@qK@wN|q-S#|l1*9P zlAhnNtSAl>CzfYP$NJ5ZGxvMG?{$K(#}H}PpOc4j$${(a$bszy z-5_kF1-gOLj+snTKIQxx^+8N6#q7#{5K-bm;GIkEy|M*1s6b$3Vdq8HJ`5rcS%_1W z`I5Iq&F#Hp9ZuzAo%71F#svLNWFuD}3c6GKU(JtHGp}xbHVSVY>Z;?0syWu{!i!=z zten-hWz=swZY?De2Jh@%`zNB(=>{axaek@bw{<(YE7};buExGz3syr7=Rn`5X7G;J$q7Hdd)|C*$4SeseNo}{RaGqQ>ZT|tVZM8A z(AV)o>XCpwSKB!kF(uhvyu(gY~C0-@tfT&6g+h{RwA^6@+e3ie0I zIIM91ostpvU_o3Nc$(Kk#G1Ls1 zQx=m~BIE3j{3s5>zN}U%(vPh{bv-UOXD1KI9|Zc?9vgFb;)oJ2z}k!QoU^{XZ6 z>bXfx_C|xcnh}ZO1I(0??7Cw=s#_NnJ?sbVFzRu*bPlTqqW6g1-k|G8hsRH%$Q{eX zKPO7imm}Xhlp}}cUKmELx<#7dFe!FJ&rR53Lsm+X5N6h7J90ZmVK<~*tx*`XB$uLC zf@4MTD1_vaVdNL-S!T3aoNiHOf3~t-Dj}Oxl|osg=-KDvU*v)G?6+n9Al`*#&6NIo zF}X4a)6!Qf|6MKrXI0FqcWH9v?t7GS+mBS{U(3kv1aaa=#j{FT#sk;$E2+mQeCzbw zpJ*>tz1n$={Ww%%iX-OFVY?msNztp6rX^zz zz$31f={cEtu3Jsrw`SrdzT_G@=%g7Dn-O$`sB@k+b-B0Z%H>|-*fn1~d-d8mio!T{ zp7jUA1Vx+EqTNrw{qgAJJan9y*(8qkPI+6<7@tuIjlXqZRqlUR|n~g~AAW;o0*jC4f pYNAOdVqeN=+D&!LeL89V8G(zLFQVDkNdKq6*Hu4`{{}lHFhFeILWckV literal 0 HcmV?d00001 diff --git a/tests/manual/rhi/multiview/texture.vert b/tests/manual/rhi/multiview/texture.vert new file mode 100644 index 00000000000..156dca0db6b --- /dev/null +++ b/tests/manual/rhi/multiview/texture.vert @@ -0,0 +1,20 @@ +#version 440 + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; + +layout(location = 0) out vec2 v_texcoord; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; + int flip; + float layer; +}; + +void main() +{ + v_texcoord = vec2(texcoord.x, texcoord.y); + if (flip != 0) + v_texcoord.y = 1.0 - v_texcoord.y; + gl_Position = mvp * position; +} diff --git a/tests/manual/rhi/multiview/texture.vert.qsb b/tests/manual/rhi/multiview/texture.vert.qsb new file mode 100644 index 0000000000000000000000000000000000000000..3af083e6a6fa15fd8ffda394992d912f2bb3c961 GIT binary patch literal 1154 zcmV-|1bzDe01f?kob6ZZZW~1u9$%U^8%Pt{CZPo~se#(DV>XTxP^~~wgJ@BuL=CF= zw{q-tv|xL!-L;8@EFtj(JO?kqTXB&PoHO&Sch+%A;SYb9XuWg3^PT(5N)bXV3ca%Q ztcXas;zG29Pk(`E1JC@4CFZOZF(7g%JTmrZ*C#B`ikBH>=(#OM!nq>i-x@E7Gcgn$ zwR1#G9MJBG!O1C2k3tM+w*>T2DsQYCJT^YRd zGw*gnxwewa7OpeZqSQYGx=L+h<0Yz^-=9A_eO3*lcD255vwm51LlAd+qb6pG2Ns@2C zFXIi!i@;cKA(vQh8Ts8I`8D|FRfhaJWVT-x@%c~mtH3y)JJ9iLH|Tdw`PDM}Ux)vU zs#EuUP1y?6l^1WKo(<&4@?BtM__zlj%zq2Ibwj_2oNtpx0%!m4D)jqL*zP?M-%~+8 z@?snNRpeGi?u>0CRs}X?-4eOh z#Fx?Rm{ykqKlB)-7$e_nORlh3LefbvGGedASgu}FrV*;+5~}LRBBfj9%H$S@QLy%v z_E#NqwpCm1q(JRwqJGc2&IytuF8ihS$!#O!iDk_UKxADa1SLhzwtYr z(2a^MyOfNN8LH4UrCu^;$|N<{S(fSq5Dn0qMWKnTGX|9Hm!Ez0`1ttwk!_TdL^24wx{f&kj)&1q1ha@MVx(F?oYNOZ zLniSlrMnh6S07a!?3E6y^A8Sd=ao-uZ`duy&8)XHs>U`}4z+E;=mnMgHVa_Zz>;-