diff options
author | Laszlo Agocs <[email protected]> | 2023-06-19 11:56:48 +0200 |
---|---|---|
committer | Laszlo Agocs <[email protected]> | 2023-06-22 15:20:26 +0200 |
commit | f9d90c6fbab1d4f66dcc3fbc06f7f157693d04d1 (patch) | |
tree | a51ead7381684a8cb484b229f162241c0481c4ad | |
parent | 2b9ef2eb44c084d39ef8324cfe1ae42a98b3038f (diff) |
rhi: Introduce multiview starting with OpenGL (ES)
Fixes: QTBUG-114770
Change-Id: Ibb1ced7f19d15a5116c60e95fd3e6b86ace63155
Reviewed-by: Andy Nichols <[email protected]>
25 files changed, 483 insertions, 6 deletions
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. */ /*! @@ -2297,6 +2313,70 @@ QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer) */ /*! + \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 \since 6.6 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<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei)>( + 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<GLint> 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<QRhiResource *> 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 Binary files differnew file mode 100644 index 00000000000..a6dd95c720b --- /dev/null +++ b/tests/manual/rhi/multiview/multiview.frag.qsb 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 @@ +<!DOCTYPE RCC><RCC version="1.0"> + <qresource> + <file>multiview.vert.qsb</file> + <file>multiview.frag.qsb</file> + <file>texture.vert.qsb</file> + <file>texture.frag.qsb</file> +</qresource> +</RCC> 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 Binary files differnew file mode 100644 index 00000000000..7eaa5ec4b51 --- /dev/null +++ b/tests/manual/rhi/multiview/multiview.vert.qsb diff --git a/tests/manual/rhi/multiview/texture.frag b/tests/manual/rhi/multiview/texture.frag new file mode 100644 index 00000000000..148f4113c84 --- /dev/null +++ b/tests/manual/rhi/multiview/texture.frag @@ -0,0 +1,19 @@ +#version 440 + +layout(location = 0) in vec2 v_texcoord; + +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2DArray tex; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; + int flip; + float layer; +}; + +void main() +{ + vec4 c = texture(tex, vec3(v_texcoord, layer)); + fragColor = vec4(c.rgb * c.a, c.a); +} diff --git a/tests/manual/rhi/multiview/texture.frag.qsb b/tests/manual/rhi/multiview/texture.frag.qsb Binary files differnew file mode 100644 index 00000000000..2193dc7306a --- /dev/null +++ b/tests/manual/rhi/multiview/texture.frag.qsb 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 Binary files differnew file mode 100644 index 00000000000..3af083e6a6f --- /dev/null +++ b/tests/manual/rhi/multiview/texture.vert.qsb diff --git a/tests/manual/rhi/rhi.pro b/tests/manual/rhi/rhi.pro index 9238de6fc20..c688c6aa176 100644 --- a/tests/manual/rhi/rhi.pro +++ b/tests/manual/rhi/rhi.pro @@ -22,7 +22,17 @@ SUBDIRS += \ computeimage \ instancing \ noninstanced \ - tex3d + tex3d \ + texturearray \ + polygonmode \ + tessellation \ + geometryshader \ + stenciloutline \ + stereo \ + tex1d \ + displacement \ + imguirenderer \ + multiview qtConfig(widgets) { SUBDIRS += \ |